You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

146 lines
4.3 KiB

import {geoAzimuthalEquidistantRaw as azimuthalEquidistantRaw, geoProjectionMutator as projectionMutator} from "d3-geo";
import {abs, asin, atan2, cos, degrees, epsilon, epsilon2, halfPi, pi, radians, round, sin, sqrt} from "./math.js";
export function gingeryRaw(rho, n) {
var k = 2 * pi / n,
rho2 = rho * rho;
function forward(lambda, phi) {
var p = azimuthalEquidistantRaw(lambda, phi),
x = p[0],
y = p[1],
r2 = x * x + y * y;
if (r2 > rho2) {
var r = sqrt(r2),
theta = atan2(y, x),
theta0 = k * round(theta / k),
alpha = theta - theta0,
rhoCosAlpha = rho * cos(alpha),
k_ = (rho * sin(alpha) - alpha * sin(rhoCosAlpha)) / (halfPi - rhoCosAlpha),
s_ = gingeryLength(alpha, k_),
e = (pi - rho) / gingeryIntegrate(s_, rhoCosAlpha, pi);
x = r;
var i = 50, delta;
do {
x -= delta = (rho + gingeryIntegrate(s_, rhoCosAlpha, x) * e - r) / (s_(x) * e);
} while (abs(delta) > epsilon && --i > 0);
y = alpha * sin(x);
if (x < halfPi) y -= k_ * (x - halfPi);
var s = sin(theta0),
c = cos(theta0);
p[0] = x * c - y * s;
p[1] = x * s + y * c;
}
return p;
}
forward.invert = function(x, y) {
var r2 = x * x + y * y;
if (r2 > rho2) {
var r = sqrt(r2),
theta = atan2(y, x),
theta0 = k * round(theta / k),
dTheta = theta - theta0;
x = r * cos(dTheta);
y = r * sin(dTheta);
var x_halfPi = x - halfPi,
sinx = sin(x),
alpha = y / sinx,
delta = x < halfPi ? Infinity : 0,
i = 10;
while (true) {
var rhosinAlpha = rho * sin(alpha),
rhoCosAlpha = rho * cos(alpha),
sinRhoCosAlpha = sin(rhoCosAlpha),
halfPi_RhoCosAlpha = halfPi - rhoCosAlpha,
k_ = (rhosinAlpha - alpha * sinRhoCosAlpha) / halfPi_RhoCosAlpha,
s_ = gingeryLength(alpha, k_);
if (abs(delta) < epsilon2 || !--i) break;
alpha -= delta = (alpha * sinx - k_ * x_halfPi - y) / (
sinx - x_halfPi * 2 * (
halfPi_RhoCosAlpha * (rhoCosAlpha + alpha * rhosinAlpha * cos(rhoCosAlpha) - sinRhoCosAlpha) -
rhosinAlpha * (rhosinAlpha - alpha * sinRhoCosAlpha)
) / (halfPi_RhoCosAlpha * halfPi_RhoCosAlpha));
}
r = rho + gingeryIntegrate(s_, rhoCosAlpha, x) * (pi - rho) / gingeryIntegrate(s_, rhoCosAlpha, pi);
theta = theta0 + alpha;
x = r * cos(theta);
y = r * sin(theta);
}
return azimuthalEquidistantRaw.invert(x, y);
};
return forward;
}
function gingeryLength(alpha, k) {
return function(x) {
var y_ = alpha * cos(x);
if (x < halfPi) y_ -= k;
return sqrt(1 + y_ * y_);
};
}
// Numerical integration: trapezoidal rule.
function gingeryIntegrate(f, a, b) {
var n = 50,
h = (b - a) / n,
s = f(a) + f(b);
for (var i = 1, x = a; i < n; ++i) s += 2 * f(x += h);
return s * 0.5 * h;
}
export default function() {
var n = 6,
rho = 30 * radians,
cRho = cos(rho),
sRho = sin(rho),
m = projectionMutator(gingeryRaw),
p = m(rho, n),
stream_ = p.stream,
epsilon = 1e-2,
cr = -cos(epsilon * radians),
sr = sin(epsilon * radians);
p.radius = function(_) {
if (!arguments.length) return rho * degrees;
cRho = cos(rho = _ * radians);
sRho = sin(rho);
return m(rho, n);
};
p.lobes = function(_) {
if (!arguments.length) return n;
return m(rho, n = +_);
};
p.stream = function(stream) {
var rotate = p.rotate(),
rotateStream = stream_(stream),
sphereStream = (p.rotate([0, 0]), stream_(stream));
p.rotate(rotate);
rotateStream.sphere = function() {
sphereStream.polygonStart(), sphereStream.lineStart();
for (var i = 0, delta = 2 * pi / n, phi = 0; i < n; ++i, phi -= delta) {
sphereStream.point(atan2(sr * cos(phi), cr) * degrees, asin(sr * sin(phi)) * degrees);
sphereStream.point(atan2(sRho * cos(phi - delta / 2), cRho) * degrees, asin(sRho * sin(phi - delta / 2)) * degrees);
}
sphereStream.lineEnd(), sphereStream.polygonEnd();
};
return rotateStream;
};
return p
.rotate([90, -40])
.scale(91.7095)
.clipAngle(180 - 1e-3);
}