| 1 | import {Adder} from "d3-array";
|
|---|
| 2 | import {cartesian, cartesianCross, cartesianNormalizeInPlace} from "./cartesian.js";
|
|---|
| 3 | import {abs, asin, atan2, cos, epsilon, epsilon2, halfPi, pi, quarterPi, sign, sin, tau} from "./math.js";
|
|---|
| 4 |
|
|---|
| 5 | function longitude(point) {
|
|---|
| 6 | return abs(point[0]) <= pi ? point[0] : sign(point[0]) * ((abs(point[0]) + pi) % tau - pi);
|
|---|
| 7 | }
|
|---|
| 8 |
|
|---|
| 9 | export default function(polygon, point) {
|
|---|
| 10 | var lambda = longitude(point),
|
|---|
| 11 | phi = point[1],
|
|---|
| 12 | sinPhi = sin(phi),
|
|---|
| 13 | normal = [sin(lambda), -cos(lambda), 0],
|
|---|
| 14 | angle = 0,
|
|---|
| 15 | winding = 0;
|
|---|
| 16 |
|
|---|
| 17 | var sum = new Adder();
|
|---|
| 18 |
|
|---|
| 19 | if (sinPhi === 1) phi = halfPi + epsilon;
|
|---|
| 20 | else if (sinPhi === -1) phi = -halfPi - epsilon;
|
|---|
| 21 |
|
|---|
| 22 | for (var i = 0, n = polygon.length; i < n; ++i) {
|
|---|
| 23 | if (!(m = (ring = polygon[i]).length)) continue;
|
|---|
| 24 | var ring,
|
|---|
| 25 | m,
|
|---|
| 26 | point0 = ring[m - 1],
|
|---|
| 27 | lambda0 = longitude(point0),
|
|---|
| 28 | phi0 = point0[1] / 2 + quarterPi,
|
|---|
| 29 | sinPhi0 = sin(phi0),
|
|---|
| 30 | cosPhi0 = cos(phi0);
|
|---|
| 31 |
|
|---|
| 32 | for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
|
|---|
| 33 | var point1 = ring[j],
|
|---|
| 34 | lambda1 = longitude(point1),
|
|---|
| 35 | phi1 = point1[1] / 2 + quarterPi,
|
|---|
| 36 | sinPhi1 = sin(phi1),
|
|---|
| 37 | cosPhi1 = cos(phi1),
|
|---|
| 38 | delta = lambda1 - lambda0,
|
|---|
| 39 | sign = delta >= 0 ? 1 : -1,
|
|---|
| 40 | absDelta = sign * delta,
|
|---|
| 41 | antimeridian = absDelta > pi,
|
|---|
| 42 | k = sinPhi0 * sinPhi1;
|
|---|
| 43 |
|
|---|
| 44 | sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
|
|---|
| 45 | angle += antimeridian ? delta + sign * tau : delta;
|
|---|
| 46 |
|
|---|
| 47 | // Are the longitudes either side of the point’s meridian (lambda),
|
|---|
| 48 | // and are the latitudes smaller than the parallel (phi)?
|
|---|
| 49 | if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
|
|---|
| 50 | var arc = cartesianCross(cartesian(point0), cartesian(point1));
|
|---|
| 51 | cartesianNormalizeInPlace(arc);
|
|---|
| 52 | var intersection = cartesianCross(normal, arc);
|
|---|
| 53 | cartesianNormalizeInPlace(intersection);
|
|---|
| 54 | var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
|
|---|
| 55 | if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
|
|---|
| 56 | winding += antimeridian ^ delta >= 0 ? 1 : -1;
|
|---|
| 57 | }
|
|---|
| 58 | }
|
|---|
| 59 | }
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | // First, determine whether the South pole is inside or outside:
|
|---|
| 63 | //
|
|---|
| 64 | // It is inside if:
|
|---|
| 65 | // * the polygon winds around it in a clockwise direction.
|
|---|
| 66 | // * the polygon does not (cumulatively) wind around it, but has a negative
|
|---|
| 67 | // (counter-clockwise) area.
|
|---|
| 68 | //
|
|---|
| 69 | // Second, count the (signed) number of times a segment crosses a lambda
|
|---|
| 70 | // from the point to the South pole. If it is zero, then the point is the
|
|---|
| 71 | // same side as the South pole.
|
|---|
| 72 |
|
|---|
| 73 | return (angle < -epsilon || angle < epsilon && sum < -epsilon2) ^ (winding & 1);
|
|---|
| 74 | }
|
|---|