52 if ( lon == -1.0 * M_PI )
54 if ( lon == -2.0 * M_PI )
57 if ( lon > 2.0 * M_PI )
58 lon = remainder(lon, 2.0 * M_PI);
60 if ( lon < -2.0 * M_PI )
61 lon = remainder(lon, -2.0 * M_PI);
64 lon = -2.0 * M_PI + lon;
66 if ( lon < -1.0 * M_PI )
67 lon = 2.0 * M_PI + lon;
69 if ( lon == -2.0 * M_PI )
81 if ( lat > 2.0 * M_PI )
82 lat = remainder(lat, 2.0 * M_PI);
84 if ( lat < -2.0 * M_PI )
85 lat = remainder(lat, -2.0 * M_PI);
90 if ( lat < -1.0 * M_PI )
91 lat = -1.0 * M_PI - lat;
96 if ( lat < -1.0 * M_PI_2 )
97 lat = -1.0 * M_PI - lat;
109 lon = remainder(lon, 360.0);
112 lon = remainder(lon, -360.0);
137 lat = remainder(lat, 360.0);
140 lat = remainder(lat, -360.0);
162 double lon = p->
lon + shift;
164 p->
lon = -1.0 * M_PI + (lon - M_PI);
192 double zmin = FLT_MAX;
193 double zmax = -1 * FLT_MAX;
198 memcpy(d, &(gbox->
xmin), 6*
sizeof(
double));
201 for ( i = 0; i < 8; i++ )
204 pt.
y = d[2 + (i % 4) / 2];
205 pt.
z = d[4 + (i % 2)];
207 if ( pt.
z < zmin ) zmin = pt.
z;
208 if ( pt.
z > zmax ) zmax = pt.
z;
210 return asin(zmax) - asin(zmin);
225 memcpy(d, &(gbox->
xmin), 6*
sizeof(
double));
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;
236 for ( j = 0; j < 2; j++ )
238 maxangle = -1 * FLT_MAX;
239 for ( i = 0; i < 4; i++ )
241 double angle, dotprod;
245 pt_n.
y = d[2 + (i % 2)];
246 magnitude = sqrt(pt_n.
x*pt_n.
x + pt_n.
y*pt_n.
y);
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 )
276 memcpy(d, &(gbox->
xmin), 6*
sizeof(
double));
279 pt.
x = pt.
y = pt.
z = 0.0;
281 for ( i = 0; i < 8; i++ )
286 pt_n.
y = d[2 + ((i % 4) / 2)];
287 pt_n.
z = d[4 + (i % 2)];
322 if (gbox->
xmin < 0.0 && gbox->
xmax > 0.0 &&
323 gbox->
ymin < 0.0 && gbox->
ymax > 0.0)
326 if ((gbox->
zmin > 0.0) && (gbox->
zmax > 0.0))
328 LWDEBUG(4,
"enclosed positive z axis");
332 else if ((gbox->
zmin < 0.0) && (gbox->
zmax < 0.0))
334 LWDEBUG(4,
"enclosed negative z axis");
340 LWDEBUG(4,
"enclosed both z axes");
348 if (gbox->
xmin < 0.0 && gbox->
xmax > 0.0 &&
349 gbox->
zmin < 0.0 && gbox->
zmax > 0.0)
351 if ((gbox->
ymin > 0.0) && (gbox->
ymax > 0.0))
353 LWDEBUG(4,
"enclosed positive y axis");
356 else if ((gbox->
ymin < 0.0) && (gbox->
ymax < 0.0))
358 LWDEBUG(4,
"enclosed negative y axis");
363 LWDEBUG(4,
"enclosed both y axes");
371 if (gbox->
ymin < 0.0 && gbox->
ymax > 0.0 &&
372 gbox->
zmin < 0.0 && gbox->
zmax > 0.0)
374 if ((gbox->
xmin > 0.0) && (gbox->
xmax > 0.0))
376 LWDEBUG(4,
"enclosed positive x axis");
379 else if ((gbox->
xmin < 0.0) && (gbox->
xmax < 0.0))
381 LWDEBUG(4,
"enclosed negative x axis");
386 LWDEBUG(4,
"enclosed both x axes");
402 p->
x = cos(g->
lat) * cos(g->
lon);
403 p->
y = cos(g->
lat) * sin(g->
lon);
412 g->
lon = atan2(p->
y, p->
x);
421 double x_rad = M_PI * g->
x / 180.0;
422 double y_rad = M_PI * g->
y / 180.0;
423 double cos_y_rad = cos(y_rad);
424 p->
x = cos_y_rad * cos(x_rad);
425 p->
y = cos_y_rad * sin(x_rad);
444 return (p1->
x*p2->
x) + (p1->
y*p2->
y) + (p1->
z*p2->
z);
452 n->
x = a->
y * b->
z - a->
z * b->
y;
453 n->
y = a->
z * b->
x - a->
x * b->
z;
454 n->
z = a->
x * b->
y - a->
y * b->
x;
522 double d = sqrt(p->
x*p->
x + p->
y*p->
y);
549 else if ( p_dot > 0.95 )
572 double cos_a = cos(angle);
573 double sin_a = sin(angle);
574 double uxuy, uyuz, uxuz;
575 double ux2, uy2, uz2;
576 double rxx, rxy, rxz, ryx, ryy, ryz, rzx, rzy, rzz;
589 rxx = cos_a + ux2 * (1 - cos_a);
590 rxy = uxuy * (1 - cos_a) - u.
z * sin_a;
591 rxz = uxuz * (1 - cos_a) + u.
y * sin_a;
593 ryx = uxuy * (1 - cos_a) + u.
z * sin_a;
594 ryy = cos_a + uy2 * (1 - cos_a);
595 ryz = uyuz * (1 - cos_a) - u.
x * sin_a;
597 rzx = uxuz * (1 - cos_a) - u.
y * sin_a;
598 rzy = uyuz * (1 - cos_a) + u.
x * sin_a;
599 rzz = cos_a + uz2 * (1 - cos_a);
601 n->
x = rxx * v1->
x + rxy * v1->
y + rxz * v1->
z;
602 n->
y = ryx * v1->
x + ryy * v1->
y + ryz * v1->
z;
603 n->
z = rzx * v1->
x + rzy * v1->
y + rzz * v1->
z;
613 double d = sqrt(p->
x*p->
x + p->
y*p->
y + p->
z*p->
z);
616 p->
x = p->
y = p->
z = 0.0;
632 double lon_qpp = (q->
lon + p->
lon) / -2.0;
633 double lon_qmp = (q->
lon - p->
lon) / 2.0;
634 double sin_p_lat_minus_q_lat = sin(p->
lat-q->
lat);
635 double sin_p_lat_plus_q_lat = sin(p->
lat+q->
lat);
636 double sin_lon_qpp = sin(lon_qpp);
637 double sin_lon_qmp = sin(lon_qmp);
638 double cos_lon_qpp = cos(lon_qpp);
639 double cos_lon_qmp = cos(lon_qmp);
640 a->
x = sin_p_lat_minus_q_lat * sin_lon_qpp * cos_lon_qmp -
641 sin_p_lat_plus_q_lat * cos_lon_qpp * sin_lon_qmp;
642 a->
y = sin_p_lat_minus_q_lat * cos_lon_qpp * cos_lon_qmp +
643 sin_p_lat_plus_q_lat * sin_lon_qpp * sin_lon_qmp;
666 double ss = fabs(s->
lon);
667 double ee = fabs(e->
lon);
668 if ( sign_s == sign_e )
703 LWDEBUG(4,
"point is on plane (dot product is zero)");
739 double angle_a, angle_b, angle_c;
740 double area_radians = 0.0;
748 area_radians = angle_a + angle_b + angle_c - M_PI;
760 return side * area_radians;
787 double vs_dot_vcp, vp_dot_vcp;
791 if ( vs.
x == -1.0 * ve.
x && vs.
y == -1.0 * ve.
y && vs.
z == -1.0 * ve.
z )
799 LWDEBUGF(4,
"vs_dot_vcp %.19g",vs_dot_vcp);
802 LWDEBUGF(4,
"vp_dot_vcp %.19g",vp_dot_vcp);
804 LWDEBUGF(4,
"fabs(vp_dot_vcp - vs_dot_vcp) %.39g",fabs(vp_dot_vcp - vs_dot_vcp));
819 if ( vp_dot_vcp > vs_dot_vcp || fabs(vp_dot_vcp - vs_dot_vcp) < 2e-16 )
821 LWDEBUG(4,
"point is in cone");
824 LWDEBUG(4,
"point is not in cone");
835 double slon = fabs((e->
start).lon) + fabs((e->
end).lon);
836 double dlon = fabs(fabs((e->
start).lon) - fabs((e->
end).lon));
837 double slat = (e->
start).lat + (e->
end).lat;
840 LWDEBUGF(4,
"e.end == GPOINT(%.6g %.6g) ", (e->
end).lat, (e->
end).lon);
850 LWDEBUG(4,
"vertical plane, we need to do this calculation in latitude");
869 LWDEBUG(4,
"over the pole...");
888 LWDEBUG(4,
"north or south?...");
893 LWDEBUG(4,
"over the north pole...");
902 LWDEBUG(4,
"over the south pole...");
913 LWDEBUG(4,
"crosses dateline, flip longitudes...");
932 LWDEBUG(4,
"true, this edge contains point");
936 LWDEBUG(4,
"false, this edge does not contain point");
946 double d_lon = e->
lon - s->
lon;
947 double cos_d_lon = cos(d_lon);
948 double cos_lat_e = cos(e->
lat);
949 double sin_lat_e = sin(e->
lat);
950 double cos_lat_s = cos(s->
lat);
951 double sin_lat_s = sin(s->
lat);
953 double a1 =
POW2(cos_lat_e * sin(d_lon));
954 double a2 =
POW2(cos_lat_s * sin_lat_e - sin_lat_s * cos_lat_e * cos_d_lon);
955 double a = sqrt(a1 + a2);
956 double b = sin_lat_s * sin_lat_e + cos_lat_s * cos_lat_e * cos_d_lon;
973 double heading = 0.0;
978 return (s->
lat > 0.0) ? M_PI : 0.0;
980 f = (sin(e->
lat) - sin(s->
lat) * cos(d)) / (sin(d) * cos(s->
lat));
985 else if ( fabs(f) > 1.0 )
993 if ( sin(e->
lon - s->
lon) < 0.0 )
994 heading = -1 * heading;
1018 double sign =
SIGNUM(hcb-hca);
1019 double ss = (a_dist + b_dist + c_dist) / 2.0;
1020 double E = tan(ss/2.0)*tan((ss-a_dist)/2.0)*tan((ss-b_dist)/2.0)*tan((ss-c_dist)/2.0);
1021 return 4.0 * atan(sqrt(fabs(E))) * sign;
1035 LWDEBUG(4,
"point is on edge");
1038 LWDEBUG(4,
"point is not on edge");
1048 double tlat = acos(z);
1049 LWDEBUGF(4,
"inputs: z(%.8g) sign(%.8g) tlat(%.8g)", z, sign, tlat);
1052 if (top)
return M_PI_2;
1053 else return -1.0 * M_PI_2;
1055 if (fabs(tlat) > M_PI_2 )
1057 tlat = sign * (M_PI - fabs(tlat));
1063 LWDEBUGF(4,
"output: tlat(%.8g)", tlat);
1076 LWDEBUG(4,
"entering function");
1079 LWDEBUGF(4,
"unit normal t1 == POINT(%.8g %.8g %.8g)", t1.
x, t1.
y, t1.
z);
1080 LWDEBUGF(4,
"unit normal t2 == POINT(%.8g %.8g %.8g)", t2.
x, t2.
y, t2.
z);
1087 LWDEBUGF(4,
"clairaut top == GPOINT(%.6g %.6g)", g_top->
lat, g_top->
lon);
1088 LWDEBUGF(4,
"clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->
lat, g_bottom->
lon);
1101 LWDEBUG(4,
"entering function");
1106 LWDEBUGF(4,
"unit normal t1 == POINT(%.8g %.8g %.8g)", t1.
x, t1.
y, t1.
z);
1107 LWDEBUGF(4,
"unit normal t2 == POINT(%.8g %.8g %.8g)", t2.
x, t2.
y, t2.
z);
1114 LWDEBUGF(4,
"clairaut top == GPOINT(%.6g %.6g)", g_top->
lat, g_top->
lon);
1115 LWDEBUGF(4,
"clairaut bottom == GPOINT(%.6g %.6g)", g_bottom->
lat, g_bottom->
lon);
1157 LWDEBUGF(4,
"e1 cross product == POINT(%.12g %.12g %.12g)", ea.
x, ea.
y, ea.
z);
1158 LWDEBUGF(4,
"e2 cross product == POINT(%.12g %.12g %.12g)", eb.
x, eb.
y, eb.
z);
1188 LWDEBUGF(4,
"v == POINT(%.12g %.12g %.12g)", v.
x, v.
y, v.
z);
1189 g->
lat = atan2(v.
z, sqrt(v.
x * v.
x + v.
y * v.
y));
1190 g->
lon = atan2(v.
y, v.
x);
1199 LWDEBUG(4,
"flipping point to other side of sphere");
1202 if ( g->
lon > M_PI )
1204 g->
lon = -1.0 * (2.0 * M_PI - g->
lon);
1216 double d1 = 1000000000.0, d2, d3, d_nearest;
1223 *closest = e->
start;
1244 if ( d2 < d_nearest )
1247 g_nearest = e->
start;
1249 if ( d3 < d_nearest )
1255 *closest = g_nearest;
1299 if ( closest1 ) *closest1 = c1;
1300 if ( closest2 ) *closest2 = c2;
1313 double lat1 = r->
lat;
1314 double lon1 = r->
lon;
1317 lat2 = asin(sin(lat1)*cos(d) + cos(lat1)*sin(d)*cos(azimuth));
1327 lon2 = lon1 + atan2(sin(azimuth)*sin(d)*cos(lat1), cos(d)-sin(lat1)*sin(lat2));
1330 if ( isnan(lat2) || isnan(lon2) )
1342 int steps = 1000000;
1351 LWDEBUG(4,
"edge is zero length. returning");
1363 LWDEBUG(4,
"edge is antipodal. setting to maximum size box, and returning");
1373 dx = (end.
x - start.
x)/steps;
1374 dy = (end.
y - start.
y)/steps;
1375 dz = (end.
z - start.
z)/steps;
1380 for ( i = 0; i < steps; i++ )
1422 lwerror(
"Antipodal (180 degrees long) edge detected!");
1437 memset(X, 0,
sizeof(
POINT3D) * 6);
1438 X[0].
x = X[2].
y = X[4].
z = 1.0;
1439 X[1].
x = X[3].
y = X[5].
z = -1.0;
1447 for ( i = 0; i < 6; i++ )
1460 Xn.
x = RX.
x * A1->
x + RX.
y * A3.
x;
1461 Xn.
y = RX.
x * A1->
y + RX.
y * A3.
y;
1462 Xn.
z = RX.
x * A1->
z + RX.
y * A3.
z;
1487 POINT3D q1, q2, qMid, qCross, qSum;
1491 if (poly->nrings < 1)
1493 pa = poly->rings[0];
1550 double grow = M_PI / 180.0 / 60.0;
1557 while ( grow < M_PI )
1561 if ( ge.
xmin > -1 ) ge.
xmin -= grow;
1562 if ( ge.
ymin > -1 ) ge.
ymin -= grow;
1563 if ( ge.
zmin > -1 ) ge.
zmin -= grow;
1564 if ( ge.
xmax < 1 ) ge.
xmax += grow;
1565 if ( ge.
ymax < 1 ) ge.
ymax += grow;
1566 if ( ge.
zmax < 1 ) ge.
zmax += grow;
1569 corners[0].
x = ge.
xmin;
1570 corners[0].
y = ge.
ymin;
1571 corners[0].
z = ge.
zmin;
1573 corners[1].
x = ge.
xmin;
1574 corners[1].
y = ge.
ymax;
1575 corners[1].
z = ge.
zmin;
1577 corners[2].
x = ge.
xmin;
1578 corners[2].
y = ge.
ymin;
1579 corners[2].
z = ge.
zmax;
1581 corners[3].
x = ge.
xmax;
1582 corners[3].
y = ge.
ymin;
1583 corners[3].
z = ge.
zmin;
1585 corners[4].
x = ge.
xmax;
1586 corners[4].
y = ge.
ymax;
1587 corners[4].
z = ge.
zmin;
1589 corners[5].
x = ge.
xmax;
1590 corners[5].
y = ge.
ymin;
1591 corners[5].
z = ge.
zmax;
1593 corners[6].
x = ge.
xmin;
1594 corners[6].
y = ge.
ymax;
1595 corners[6].
z = ge.
zmax;
1597 corners[7].
x = ge.
xmax;
1598 corners[7].
y = ge.
ymax;
1599 corners[7].
z = ge.
zmax;
1601 LWDEBUG(4,
"trying to use a box corner point...");
1602 for ( i = 0; i < 8; i++ )
1605 LWDEBUGF(4,
"testing corner %d: POINT(%.8g %.8g %.8g)", i, corners[i].
x, corners[i].
y, corners[i].z);
1608 LWDEBUGF(4,
"corner %d is outside our gbox", i);
1614 LWDEBUGF(4,
"returning POINT(%.8g %.8g) as outside point", pt_outside->
x, pt_outside->
y);
1632 double d,
double max_seg_length,
1640 if (d <= max_seg_length)
1655 mid.
x = (p1->
x + p2->
x) / 2.0;
1656 mid.
y = (p1->
y + p2->
y) / 2.0;
1657 mid.
z = (p1->
z + p2->
z) / 2.0;
1665 midv.
z = (v1->
z + v2->
z) / 2.0;
1666 midv.
m = (v1->
m + v2->
m) / 2.0;
1692 lwerror(
"%s: null input pointarray", __func__);
1693 if ( max_seg_length <= 0.0 )
1694 lwerror(
"%s: maximum segment length must be positive", __func__);
1700 for (i = 1; i < pa_in->
npoints; i++)
1714 if (d > max_seg_length)
1743 LWPOLY *lwpoly_in, *lwpoly_out;
1755 switch (lwg_in->
type)
1769 for ( i = 0; i < lwpoly_in->
nrings; i++ )
1781 for ( i = 0; i < lwcol_in->
ngeoms; i++ )
1788 lwerror(
"lwgeom_segmentize_sphere: unsupported input geometry type: %d - %s",
1793 lwerror(
"lwgeom_segmentize_sphere got to the end of the function, should not happen");
1811 if ( ! pa || pa->
npoints < 4 )
1819 for ( i = 2; i < pa->
npoints-1; i++ )
1840 int use_sphere = (s->
a == s->
b ? 1 : 0);
1861 else if ( distance < 0.95 * tolerance )
1896 for ( i = 1; i < pa_many->
npoints; i++ )
1910 if ( d < tolerance )
1918 else if ( d < tolerance * 0.95 )
1927 if ( d < tolerance )
1950 for ( i = 1; i < pa1->
npoints; i++ )
1961 for ( j = 1; j < pa2->
npoints; j++ )
1976 LWDEBUG(4,
"edge intersection! returning 0.0");
1980 LWDEBUGF(4,
"got edge_distance_to_edge %.8g", d);
1988 if ( d < tolerance )
1997 if ( d < tolerance )
2012 LWDEBUGF(4,
"finished all loops, returning %.8g", distance);
2039 type = lwgeom->
type;
2060 for ( i = 1; i < poly->
nrings; i++ )
2074 for ( i = 0; i < col->
ngeoms; i++ )
2104 if ( distance < 0.0 ) {
2110 azimuth -= 2.0 * M_PI * floor(azimuth / (2.0 * M_PI));
2113 if ( distance > (M_PI * spheroid->
radius) )
2115 lwerror(
"Distance must not be greater than %g", M_PI * spheroid->
radius);
2127 LWDEBUGF(3,
"Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2128 lwerror(
"Unable to project from (%g %g) with azimuth %g and distance %g", x, y, azimuth, distance);
2136 pt_dest.
z = pt_dest.
m = 0.0;
2155 double x1, y1, x2, y2;
2195 LWDEBUGF(4,
"entered function, tolerance %.8g", tolerance);
2204 type1 = lwgeom1->
type;
2205 type2 = lwgeom2->
type;
2208 if ( lwgeom1->
bbox )
2209 gbox1 = *(lwgeom1->
bbox);
2214 if ( lwgeom2->
bbox )
2215 gbox2 = *(lwgeom2->
bbox);
2230 pa1 = ((
LWPOINT*)lwgeom1)->point;
2232 pa1 = ((
LWLINE*)lwgeom1)->points;
2235 pa2 = ((
LWPOINT*)lwgeom2)->point;
2237 pa2 = ((
LWLINE*)lwgeom2)->points;
2255 lwpoly = (
LWPOLY*)lwgeom2;
2260 lwpoly = (
LWPOLY*)lwgeom1;
2271 for ( i = 0; i < lwpoly->
nrings; i++ )
2274 if ( ring_distance < distance )
2275 distance = ring_distance;
2276 if ( distance < tolerance )
2294 lwline = (
LWLINE*)lwgeom1;
2295 lwpoly = (
LWPOLY*)lwgeom2;
2299 lwline = (
LWLINE*)lwgeom2;
2300 lwpoly = (
LWPOLY*)lwgeom1;
2304 LWDEBUG(4,
"checking if a point of line is in polygon");
2310 LWDEBUG(4,
"checking ring distances");
2313 for ( i = 0; i < lwpoly->
nrings; i++ )
2316 LWDEBUGF(4,
"ring[%d] ring_distance = %.8g", i, ring_distance);
2317 if ( ring_distance < distance )
2318 distance = ring_distance;
2319 if ( distance < tolerance )
2322 LWDEBUGF(4,
"all rings checked, returning distance = %.8g", distance);
2348 for ( i = 0; i < lwpoly1->
nrings; i++ )
2350 for ( j = 0; j < lwpoly2->
nrings; j++ )
2353 if ( ring_distance < distance )
2354 distance = ring_distance;
2355 if ( distance < tolerance )
2369 for ( i = 0; i < col->
ngeoms; i++ )
2372 if ( geom_distance < distance )
2373 distance = geom_distance;
2374 if ( distance < tolerance )
2387 for ( i = 0; i < col->
ngeoms; i++ )
2390 if ( geom_distance < distance )
2391 distance = geom_distance;
2392 if ( distance < tolerance )
2414 type1 = lwgeom1->
type;
2415 type2 = lwgeom2->
type;
2422 LWDEBUG(4,
"dimension of geom2 is bigger than geom1");
2427 if ( lwgeom1->
bbox )
2428 gbox1 = *(lwgeom1->
bbox);
2433 if ( lwgeom2->
bbox )
2434 gbox2 = *(lwgeom2->
bbox);
2473 for ( i = 0; i < col->
ngeoms; i++ )
2489 for ( i = 0; i < col->
ngeoms; i++ )
2500 lwerror(
"lwgeom_covers_lwgeom_sphere: reached end of function without resolution");
2513 int in_hole_count = 0;
2523 LWDEBUG(4,
"returning false, geometry is empty or null");
2529 gbox = *(poly->
bbox);
2538 LWDEBUG(4,
"the point is not in the box!");
2545 LWDEBUGF(4,
"pt_outside POINT(%.18g %.18g)", pt_outside.
x, pt_outside.
y);
2546 LWDEBUGF(4,
"pt_to_test POINT(%.18g %.18g)", pt_to_test->
x, pt_to_test->
y);
2553 LWDEBUG(4,
"returning false, point is outside ring");
2557 LWDEBUGF(4,
"testing %d rings", poly->nrings);
2560 for ( i = 1; i < poly->nrings; i++ )
2562 LWDEBUGF(4,
"ring test loop %d", i);
2568 LWDEBUGF(4,
"in_hole_count == %d", in_hole_count);
2570 if ( in_hole_count % 2 )
2572 LWDEBUG(4,
"returning false, inner ring containment count is odd");
2576 LWDEBUG(4,
"returning true, inner ring containment count is even");
2592 LWDEBUG(4,
"returning false, geometry1 is empty or null");
2599 LWDEBUG(4,
"returning false, geometry2 is empty or null");
2604 for (i = 0; i < poly2->nrings; i++)
2612 LWDEBUG(4,
"returning false, geometry2 has point outside of geometry1");
2620 LWDEBUG(4,
"returning false, geometry2 has point inside a hole of geometry1");
2627 for (i = 0; i < poly2->nrings; i++)
2631 LWDEBUG(4,
"returning false, geometry2 is partially outside of geometry1");
2648 LWDEBUG(4,
"returning false, geometry1 is empty or null");
2655 LWDEBUG(4,
"returning false, geometry2 is empty or null");
2661 LWDEBUG(4,
"returning false, geometry2 has point outside of geometry1");
2668 LWDEBUG(4,
"returning false, geometry2 is partially outside of geometry1");
2682 for (i = 0; i < pta->
npoints; i++) {
2686 LWDEBUG(4,
"returning false, geometry2 has point outside of geometry1");
2702 for (i = 0; i < lwpoly->
nrings; i++)
2713 for (k = 0; k < line->
npoints - 1; k++)
2780 LWDEBUG(4,
"returning false, first point of line2 is not covered by line1");
2787 LWDEBUG(4,
"returning false, last point of line2 is not covered by line1");
2793 while (i < lwline1->points->npoints - 1 && j < lwline2->points->npoints - 1)
2831 LWDEBUG(4,
"returning false, found point not covered by both lines");
2863 assert(n < pa->npoints);
2902 for ( i = 1; i < pa->
npoints; i++ )
2949 for ( i = 0; i < poly->
nrings; i++ )
2989 for ( i = 0; i < coll->
ngeoms; i++ )
3043 lwerror(
"lwgeom_calculate_gbox_geodetic: unsupported input geometry type: %d - %s",
3063 if ( pt.
x < -180.0 || pt.
y < -90.0 || pt.
x > 180.0 || pt.
y > 90.0 )
3087 for ( i = 0; i < poly->
nrings; i++ )
3107 for ( i = 0; i < col->
ngeoms; i++ )
3138 lwerror(
"lwgeom_check_geodetic: unsupported input geometry type: %d - %s",
3152 for ( t=0; t < pa->
npoints; t++ )
3155 if ( pt.
x < -180.0 || pt.
x > 180.0 || pt.
y < -90.0 || pt.
y > 90.0 )
3184 for ( i = 0; i < poly->
nrings; i++ )
3198 for ( i = 0; i < col->
ngeoms; i++ )
3231 double za = 0.0, zb = 0.0;
3235 double length = 0.0;
3236 double seglength = 0.0;
3239 if ( ! pa || pa->
npoints < 2 )
3252 for ( i = 1; i < pa->
npoints; i++ )
3269 seglength = sqrt( (zb-za)*(zb-za) + seglength*seglength );
3272 length += seglength;
3285 double length = 0.0;
3304 for ( i = 0; i < poly->
nrings; i++ )
3318 for ( i = 0; i < col->
ngeoms; i++ )
3325 lwerror(
"unsupported type passed to lwgeom_length_sphere");
3343 static double tolerance = 1e-10;
3346 lwerror(
"ptarray_nudge_geodetic called with null input");
3348 for(i = 0; i < pa->
npoints; i++ )
3351 if ( p.
x < -180.0 && (-180.0 - p.
x < tolerance) )
3356 if ( p.
x > 180.0 && (p.
x - 180.0 < tolerance) )
3361 if ( p.
y < -90.0 && (-90.0 - p.
y < tolerance) )
3366 if ( p.
y > 90.0 && (p.
y - 90.0 < tolerance) )
3411 for ( i = 0; i < poly->
nrings; i++ )
3414 rv = (rv ==
LW_TRUE ? rv : n);
3426 for ( i = 0; i < col->
ngeoms; i++ )
3429 rv = (rv ==
LW_TRUE ? rv : n);
3446 double min_similarity, similarity;
3460 if (fabs(1.0 - min_similarity) > 1e-10)
3469 if (similarity > min_similarity)
3517 return dp < 0.0 ? -1 : 1;
3529 int a1_side, a2_side, b1_side, b2_side;
3559 if ( a1_side == a2_side && a1_side != 0 )
3566 if ( b1_side == b2_side && b1_side != 0 )
3573 if ( a1_side != a2_side && (a1_side + a2_side) == 0 &&
3574 b1_side != b2_side && (b1_side + b2_side) == 0 )
3602 else if ( a2_side == 0 )
3614 else if ( b2_side == 0 )
3636 int count = 0, i, inter;
3639 if ( ! pa || pa->
npoints < 4 )
3651 for ( i = 1; i < pa->
npoints; i++ )
3653 LWDEBUGF(4,
"testing edge (%d)", i);
3654 LWDEBUGF(4,
" start point == POINT(%.12g %.12g)", p.
x, p.
y);
3690 LWDEBUGF(4,
" edge (%d) crossed, disregarding to avoid double count", i, count);
3696 LWDEBUGF(4,
" edge (%d) crossed, count == %d", i, count);
3701 LWDEBUGF(4,
" edge (%d) did not cross", i);
3708 LWDEBUGF(4,
"final count == %d", count);
void ptarray_set_point4d(POINTARRAY *pa, int n, const POINT4D *p4d)
GBOX * gbox_copy(const GBOX *box)
Return a copy of the GBOX, based on dimensionality of flags.
int gbox_centroid(const GBOX *gbox, POINT2D *out)
Computes the average(ish) center of the box and returns success.
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.
static int lwtriangle_calculate_gbox_geodetic(const LWTRIANGLE *triangle, GBOX *gbox)
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...
double sphere_distance(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
Given two points on a unit sphere, calculate their distance apart in radians.
static int lwline_force_geodetic(LWLINE *line)
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
double longitude_radians_normalize(double lon)
Convert a longitude to the range of -PI,PI.
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 ...
char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2)
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.
Two-point great circle segment from a to b.
double gbox_angular_height(const GBOX *gbox)
Returns the angular height (latitudinal span) of the box in radians.
#define PIR_B_TOUCH_RIGHT
double spheroid_direction(const GEOGRAPHIC_POINT *r, const GEOGRAPHIC_POINT *s, const SPHEROID *spheroid)
Computes the direction of the geodesic joining two points on the spheroid.
void normalize(POINT3D *p)
Normalize to a unit vector.
static double ptarray_distance_spheroid(const POINTARRAY *pa1, const POINTARRAY *pa2, const SPHEROID *s, double tolerance, int check_intersection)
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...
int getPoint2d_p_ro(const POINTARRAY *pa, int n, POINT2D **point)
This function can only be used on LWGEOM that is built on top of GSERIALIZED, otherwise alignment err...
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...
void gbox_duplicate(const GBOX *original, GBOX *duplicate)
Copy the values of original GBOX into duplicate.
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.
char * gbox_to_string(const GBOX *gbox)
Allocate a string representation of the GBOX, based on dimensionality of flags.
static int point3d_equals(const POINT3D *p1, const POINT3D *p2)
Utility function for ptarray_contains_point_sphere()
int crosses_dateline(const GEOGRAPHIC_POINT *s, const GEOGRAPHIC_POINT *e)
#define PIR_A_TOUCH_RIGHT
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
int lwgeom_covers_lwgeom_sphere(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2)
Calculate covers predicate for two lwgeoms on the sphere.
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 ...
int lwpoly_covers_pointarray(const LWPOLY *lwpoly, const POINTARRAY *pta)
return LW_TRUE if all points are inside the polygon
double z_to_latitude(double z, int top)
Used in great circle to compute the pole of the great circle.
Datum area(PG_FUNCTION_ARGS)
char * lwgeom_to_ewkt(const LWGEOM *lwgeom)
Return an alloced string.
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
int gbox_contains_point3d(const GBOX *gbox, const POINT3D *pt)
Return true if the point is inside the gbox.
void cart2geog(const POINT3D *p, GEOGRAPHIC_POINT *g)
Convert cartesion coordinates on unit sphere to spherical coordinates.
int edge_calculate_gbox_slow(const GEOGRAPHIC_EDGE *e, GBOX *gbox)
#define LWDEBUG(level, msg)
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...
#define LW_ON_INTERRUPT(x)
#define POLYHEDRALSURFACETYPE
void point_shift(GEOGRAPHIC_POINT *p, double shift)
Shift a point around by a number of radians.
double lwgeom_length_spheroid(const LWGEOM *geom, const SPHEROID *s)
Calculate the geodetic length of a lwgeom on the unit sphere.
int gbox_init_point3d(const POINT3D *p, GBOX *gbox)
Initialize a GBOX using the values of the point.
int lwgeom_force_geodetic(LWGEOM *geom)
Force coordinates of LWGEOM into geodetic range (-180, -90, 180, 90)
static int lwline_calculate_gbox_geodetic(const LWLINE *line, GBOX *gbox)
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
double longitude_degrees_normalize(double lon)
Convert a longitude to the range of -180,180.
Point in spherical coordinates on the world.
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...
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
static double sphere_angle(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
Returns the angle in radians at point B of the triangle formed by A-B-C.
int lwpoly_pt_outside(const LWPOLY *poly, POINT2D *pt_outside)
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...
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.
double latitude_degrees_normalize(double lat)
Convert a latitude to the range of -90,90.
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...
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...
double lwpoint_get_x(const LWPOINT *point)
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...
static int lwpoint_force_geodetic(LWPOINT *point)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
static double dot_product(const POINT3D *p1, const POINT3D *p2)
Convert cartesion coordinates on unit sphere to lon/lat coordinates static void cart2ll(const POINT3D...
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
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, then a duplicate point will not be added.
const POINT2D * getPoint2d_cp(const POINTARRAY *pa, int n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from...
#define LW_TRUE
Return types for functions with status returns.
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
double edge_distance_to_point(const GEOGRAPHIC_EDGE *e, const GEOGRAPHIC_POINT *gp, GEOGRAPHIC_POINT *closest)
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...
double lwgeom_area_sphere(const LWGEOM *lwgeom, const SPHEROID *spheroid)
Calculate the area of an LWGEOM.
double ptarray_length_spheroid(const POINTARRAY *pa, const SPHEROID *s)
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. ...
void ll2cart(const POINT2D *g, POINT3D *p)
Convert lon/lat coordinates to cartesion coordinates on unit sphere.
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 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...
int lwgeom_calculate_gbox_geodetic(const LWGEOM *geom, GBOX *gbox)
Calculate the geodetic bounding box for an LWGEOM.
int gbox_merge_point3d(const POINT3D *p, GBOX *gbox)
Update the GBOX to be large enough to include itself and the new point.
static void vector_difference(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the difference of two vectors.
uint8_t * getPoint_internal(const POINTARRAY *pa, int n)
static int lwpoint_calculate_gbox_geodetic(const LWPOINT *point, GBOX *gbox)
static int lwcollection_force_geodetic(LWCOLLECTION *col)
int lwpoly_covers_lwline(const LWPOLY *poly, const LWLINE *line)
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
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!)...
static int lwpoly_pt_outside_hack(const LWPOLY *poly, POINT2D *pt_outside)
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 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 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 ptarray_calculate_gbox_geodetic(const POINTARRAY *pa, GBOX *gbox)
Calculate geodetic (x/y/z) box and add values to gbox.
int getPoint2d_p(const POINTARRAY *pa, int n, POINT2D *point)
#define FLAGS_GET_Z(flags)
Macros for manipulating the 'flags' byte.
double gbox_angular_width(const GBOX *gbox)
Returns the angular width (longitudinal span) of the box in radians.
static int lwpolygon_calculate_gbox_geodetic(const LWPOLY *poly, GBOX *gbox)
LWGEOM * lwgeom_clone(const LWGEOM *lwgeom)
Clone LWGEOM object.
Datum distance(PG_FUNCTION_ARGS)
static int lwline_check_geodetic(const LWLINE *line)
void lwgeom_set_geodetic(LWGEOM *geom, int value)
Set the FLAGS geodetic bit on geometry an all sub-geometries and pointlists.
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
uint8_t gflags(int hasz, int hasm, int geodetic)
Construct a new flags char.
void geog2cart(const GEOGRAPHIC_POINT *g, POINT3D *p)
Convert spherical coordinates to cartesion coordinates on unit sphere.
int gbox_geocentric_slow
For testing geodetic bounding box, we have a magic global variable.
void vector_sum(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the sum of two vectors.
double latitude_radians_normalize(double lat)
Convert a latitude to the range of -PI/2,PI/2.
int edge_intersects(const POINT3D *A1, const POINT3D *A2, const POINT3D *B1, const POINT3D *B2)
Returns non-zero if edges A and B interact.
double sphere_distance_cartesian(const POINT3D *s, const POINT3D *e)
Given two unit vectors, calculate their distance apart in radians.
int ptarray_has_m(const POINTARRAY *pa)
int geographic_point_equals(const GEOGRAPHIC_POINT *g1, const GEOGRAPHIC_POINT *g2)
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.
LWCOLLECTION * lwgeom_as_lwcollection(const LWGEOM *lwgeom)
static int lwcollection_check_geodetic(const LWCOLLECTION *col)
void geographic_point_init(double lon, double lat, GEOGRAPHIC_POINT *g)
Initialize a geographic point.
double ptarray_area_sphere(const POINTARRAY *pa)
Returns the area of the ring (ring must be closed) in square radians (surface of the sphere is 4*PI)...
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
static int lwtriangle_check_geodetic(const LWTRIANGLE *triangle)
double lwgeom_azumith_spheroid(const LWPOINT *r, const LWPOINT *s, const SPHEROID *spheroid)
Calculate a bearing (azimuth) given a source and destination point.
LWPOLY * lwpoly_construct_empty(int srid, char hasz, char hasm)
#define FLAGS_GET_M(flags)
int p3d_same(const POINT3D *p1, const POINT3D *p2)
#define deg2rad(d)
Conversion functions.
LWPOINT * lwline_get_lwpoint(const LWLINE *line, int where)
Returns freshly allocated LWPOINT that corresponds to the index where.
int gbox_overlaps(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps, LW_FALSE otherwise.
static double sphere_signed_area(const GEOGRAPHIC_POINT *a, const GEOGRAPHIC_POINT *b, const GEOGRAPHIC_POINT *c)
Computes the spherical area of a triangle.
double lwpoint_get_y(const LWPOINT *point)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
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 lwpoly_check_geodetic(const LWPOLY *poly)
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.
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
static int lwpoly_force_geodetic(LWPOLY *poly)
int ptarray_has_z(const POINTARRAY *pa)
#define PIR_NO_INTERACT
Bitmask elements for edge_intersects() return value.
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...
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
static int lwpoint_check_geodetic(const LWPOINT *point)
int lw_segment_side(const POINT2D *p1, const POINT2D *p2, const POINT2D *q)
lw_segment_side()
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
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.
double vector_angle(const POINT3D *v1, const POINT3D *v2)
Angle between two unit vectors.
static int gbox_check_poles(GBOX *gbox)
Check to see if this geocentric gbox is wrapped around a pole.
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...
static void cross_product(const POINT3D *a, const POINT3D *b, POINT3D *n)
Calculate the cross product of two vectors.
#define LWDEBUGF(level, msg,...)
static void normalize2d(POINT2D *p)
Normalize to a unit vector.
static int ptarray_force_geodetic(POINTARRAY *pa)
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...
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
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)
int lwgeom_check_geodetic(const LWGEOM *geom)
Check that coordinates of LWGEOM are all within the geodetic range (-180, -90, 180, 90)
int lwline_covers_lwpoint(const LWLINE *lwline, const LWPOINT *lwpoint)
return LW_TRUE if any of the line segments covers the point
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 lwerror(const char *fmt,...)
Write a notice out to the error handler.
static int lwcollection_calculate_gbox_geodetic(const LWCOLLECTION *coll, GBOX *gbox)
int getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *point)
int p4d_same(const POINT4D *p1, const POINT4D *p2)
#define SIGNUM(n)
Macro that returns: -1 if n < 0, 1 if n > 0, 0 if n == 0.
void vector_scale(POINT3D *n, double scale)
Scale a vector out by a factor.
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
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.
static int ptarray_check_geodetic(const POINTARRAY *pa)