StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
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.
5007 lines
138 KiB
5007 lines
138 KiB
4 years ago
|
// https://d3js.org/d3-geo-projection/ v2.8.1 Copyright 2020 Mike Bostock
|
||
|
(function (global, factory) {
|
||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-geo'), require('d3-array')) :
|
||
|
typeof define === 'function' && define.amd ? define(['exports', 'd3-geo', 'd3-array'], factory) :
|
||
|
(factory((global.d3 = global.d3 || {}),global.d3,global.d3));
|
||
|
}(this, (function (exports,d3Geo,d3Array) { 'use strict';
|
||
|
|
||
|
var abs = Math.abs;
|
||
|
var atan = Math.atan;
|
||
|
var atan2 = Math.atan2;
|
||
|
var cos = Math.cos;
|
||
|
var exp = Math.exp;
|
||
|
var floor = Math.floor;
|
||
|
var log = Math.log;
|
||
|
var max = Math.max;
|
||
|
var min = Math.min;
|
||
|
var pow = Math.pow;
|
||
|
var round = Math.round;
|
||
|
var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
|
||
|
var sin = Math.sin;
|
||
|
var tan = Math.tan;
|
||
|
|
||
|
var epsilon = 1e-6;
|
||
|
var epsilon2 = 1e-12;
|
||
|
var pi = Math.PI;
|
||
|
var halfPi = pi / 2;
|
||
|
var quarterPi = pi / 4;
|
||
|
var sqrt1_2 = Math.SQRT1_2;
|
||
|
var sqrt2 = sqrt(2);
|
||
|
var sqrtPi = sqrt(pi);
|
||
|
var tau = pi * 2;
|
||
|
var degrees = 180 / pi;
|
||
|
var radians = pi / 180;
|
||
|
|
||
|
function sinci(x) {
|
||
|
return x ? x / Math.sin(x) : 1;
|
||
|
}
|
||
|
|
||
|
function asin(x) {
|
||
|
return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
|
||
|
}
|
||
|
|
||
|
function acos(x) {
|
||
|
return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
|
||
|
}
|
||
|
|
||
|
function sqrt(x) {
|
||
|
return x > 0 ? Math.sqrt(x) : 0;
|
||
|
}
|
||
|
|
||
|
function tanh(x) {
|
||
|
x = exp(2 * x);
|
||
|
return (x - 1) / (x + 1);
|
||
|
}
|
||
|
|
||
|
function sinh(x) {
|
||
|
return (exp(x) - exp(-x)) / 2;
|
||
|
}
|
||
|
|
||
|
function cosh(x) {
|
||
|
return (exp(x) + exp(-x)) / 2;
|
||
|
}
|
||
|
|
||
|
function arsinh(x) {
|
||
|
return log(x + sqrt(x * x + 1));
|
||
|
}
|
||
|
|
||
|
function arcosh(x) {
|
||
|
return log(x + sqrt(x * x - 1));
|
||
|
}
|
||
|
|
||
|
function airyRaw(beta) {
|
||
|
var tanBeta_2 = tan(beta / 2),
|
||
|
b = 2 * log(cos(beta / 2)) / (tanBeta_2 * tanBeta_2);
|
||
|
|
||
|
function forward(x, y) {
|
||
|
var cosx = cos(x),
|
||
|
cosy = cos(y),
|
||
|
siny = sin(y),
|
||
|
cosz = cosy * cosx,
|
||
|
k = -((1 - cosz ? log((1 + cosz) / 2) / (1 - cosz) : -0.5) + b / (1 + cosz));
|
||
|
return [k * cosy * sin(x), k * siny];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var r = sqrt(x * x + y * y),
|
||
|
z = -beta / 2,
|
||
|
i = 50, delta;
|
||
|
if (!r) return [0, 0];
|
||
|
do {
|
||
|
var z_2 = z / 2,
|
||
|
cosz_2 = cos(z_2),
|
||
|
sinz_2 = sin(z_2),
|
||
|
tanz_2 = tan(z_2),
|
||
|
lnsecz_2 = log(1 / cosz_2);
|
||
|
z -= delta = (2 / tanz_2 * lnsecz_2 - b * tanz_2 - r) / (-lnsecz_2 / (sinz_2 * sinz_2) + 1 - b / (2 * cosz_2 * cosz_2));
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
var sinz = sin(z);
|
||
|
return [atan2(x * sinz, r * cos(z)), asin(y * sinz / r)];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function airy() {
|
||
|
var beta = halfPi,
|
||
|
m = d3Geo.geoProjectionMutator(airyRaw),
|
||
|
p = m(beta);
|
||
|
|
||
|
p.radius = function(_) {
|
||
|
return arguments.length ? m(beta = _ * radians) : beta * degrees;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(179.976)
|
||
|
.clipAngle(147);
|
||
|
}
|
||
|
|
||
|
function aitoffRaw(x, y) {
|
||
|
var cosy = cos(y), sincia = sinci(acos(cosy * cos(x /= 2)));
|
||
|
return [2 * cosy * sin(x) * sincia, sin(y) * sincia];
|
||
|
}
|
||
|
|
||
|
// Abort if [x, y] is not within an ellipse centered at [0, 0] with
|
||
|
// semi-major axis pi and semi-minor axis pi/2.
|
||
|
aitoffRaw.invert = function(x, y) {
|
||
|
if (x * x + 4 * y * y > pi * pi + epsilon) return;
|
||
|
var x1 = x, y1 = y, i = 25;
|
||
|
do {
|
||
|
var sinx = sin(x1),
|
||
|
sinx_2 = sin(x1 / 2),
|
||
|
cosx_2 = cos(x1 / 2),
|
||
|
siny = sin(y1),
|
||
|
cosy = cos(y1),
|
||
|
sin_2y = sin(2 * y1),
|
||
|
sin2y = siny * siny,
|
||
|
cos2y = cosy * cosy,
|
||
|
sin2x_2 = sinx_2 * sinx_2,
|
||
|
c = 1 - cos2y * cosx_2 * cosx_2,
|
||
|
e = c ? acos(cosy * cosx_2) * sqrt(f = 1 / c) : f = 0,
|
||
|
f,
|
||
|
fx = 2 * e * cosy * sinx_2 - x,
|
||
|
fy = e * siny - y,
|
||
|
dxdx = f * (cos2y * sin2x_2 + e * cosy * cosx_2 * sin2y),
|
||
|
dxdy = f * (0.5 * sinx * sin_2y - e * 2 * siny * sinx_2),
|
||
|
dydx = f * 0.25 * (sin_2y * sinx_2 - e * siny * cos2y * sinx),
|
||
|
dydy = f * (sin2y * cosx_2 + e * sin2x_2 * cosy),
|
||
|
z = dxdy * dydx - dydy * dxdx;
|
||
|
if (!z) break;
|
||
|
var dx = (fy * dxdy - fx * dydy) / z,
|
||
|
dy = (fx * dydx - fy * dxdx) / z;
|
||
|
x1 -= dx, y1 -= dy;
|
||
|
} while ((abs(dx) > epsilon || abs(dy) > epsilon) && --i > 0);
|
||
|
return [x1, y1];
|
||
|
};
|
||
|
|
||
|
function aitoff() {
|
||
|
return d3Geo.geoProjection(aitoffRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function armadilloRaw(phi0) {
|
||
|
var sinPhi0 = sin(phi0),
|
||
|
cosPhi0 = cos(phi0),
|
||
|
sPhi0 = phi0 >= 0 ? 1 : -1,
|
||
|
tanPhi0 = tan(sPhi0 * phi0),
|
||
|
k = (1 + sinPhi0 - cosPhi0) / 2;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var cosPhi = cos(phi),
|
||
|
cosLambda = cos(lambda /= 2);
|
||
|
return [
|
||
|
(1 + cosPhi) * sin(lambda),
|
||
|
(sPhi0 * phi > -atan2(cosLambda, tanPhi0) - 1e-3 ? 0 : -sPhi0 * 10) + k + sin(phi) * cosPhi0 - (1 + cosPhi) * sinPhi0 * cosLambda // TODO D3 core should allow null or [NaN, NaN] to be returned.
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var lambda = 0,
|
||
|
phi = 0,
|
||
|
i = 50;
|
||
|
do {
|
||
|
var cosLambda = cos(lambda),
|
||
|
sinLambda = sin(lambda),
|
||
|
cosPhi = cos(phi),
|
||
|
sinPhi = sin(phi),
|
||
|
A = 1 + cosPhi,
|
||
|
fx = A * sinLambda - x,
|
||
|
fy = k + sinPhi * cosPhi0 - A * sinPhi0 * cosLambda - y,
|
||
|
dxdLambda = A * cosLambda / 2,
|
||
|
dxdPhi = -sinLambda * sinPhi,
|
||
|
dydLambda = sinPhi0 * A * sinLambda / 2,
|
||
|
dydPhi = cosPhi0 * cosPhi + sinPhi0 * cosLambda * sinPhi,
|
||
|
denominator = dxdPhi * dydLambda - dydPhi * dxdLambda,
|
||
|
dLambda = (fy * dxdPhi - fx * dydPhi) / denominator / 2,
|
||
|
dPhi = (fx * dydLambda - fy * dxdLambda) / denominator;
|
||
|
lambda -= dLambda, phi -= dPhi;
|
||
|
} while ((abs(dLambda) > epsilon || abs(dPhi) > epsilon) && --i > 0);
|
||
|
return sPhi0 * phi > -atan2(cos(lambda), tanPhi0) - 1e-3 ? [lambda * 2, phi] : null;
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function armadillo() {
|
||
|
var phi0 = 20 * radians,
|
||
|
sPhi0 = phi0 >= 0 ? 1 : -1,
|
||
|
tanPhi0 = tan(sPhi0 * phi0),
|
||
|
m = d3Geo.geoProjectionMutator(armadilloRaw),
|
||
|
p = m(phi0),
|
||
|
stream_ = p.stream;
|
||
|
|
||
|
p.parallel = function(_) {
|
||
|
if (!arguments.length) return phi0 * degrees;
|
||
|
tanPhi0 = tan((sPhi0 = (phi0 = _ * radians) >= 0 ? 1 : -1) * phi0);
|
||
|
return m(phi0);
|
||
|
};
|
||
|
|
||
|
p.stream = function(stream) {
|
||
|
var rotate = p.rotate(),
|
||
|
rotateStream = stream_(stream),
|
||
|
sphereStream = (p.rotate([0, 0]), stream_(stream)),
|
||
|
precision = p.precision();
|
||
|
p.rotate(rotate);
|
||
|
rotateStream.sphere = function() {
|
||
|
sphereStream.polygonStart(), sphereStream.lineStart();
|
||
|
for (var lambda = sPhi0 * -180; sPhi0 * lambda < 180; lambda += sPhi0 * 90)
|
||
|
sphereStream.point(lambda, sPhi0 * 90);
|
||
|
if (phi0) while (sPhi0 * (lambda -= 3 * sPhi0 * precision) >= -180) {
|
||
|
sphereStream.point(lambda, sPhi0 * -atan2(cos(lambda * radians / 2), tanPhi0) * degrees);
|
||
|
}
|
||
|
sphereStream.lineEnd(), sphereStream.polygonEnd();
|
||
|
};
|
||
|
return rotateStream;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(218.695)
|
||
|
.center([0, 28.0974]);
|
||
|
}
|
||
|
|
||
|
function augustRaw(lambda, phi) {
|
||
|
var tanPhi = tan(phi / 2),
|
||
|
k = sqrt(1 - tanPhi * tanPhi),
|
||
|
c = 1 + k * cos(lambda /= 2),
|
||
|
x = sin(lambda) * k / c,
|
||
|
y = tanPhi / c,
|
||
|
x2 = x * x,
|
||
|
y2 = y * y;
|
||
|
return [
|
||
|
4 / 3 * x * (3 + x2 - 3 * y2),
|
||
|
4 / 3 * y * (3 + 3 * x2 - y2)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
augustRaw.invert = function(x, y) {
|
||
|
x *= 3 / 8, y *= 3 / 8;
|
||
|
if (!x && abs(y) > 1) return null;
|
||
|
var x2 = x * x,
|
||
|
y2 = y * y,
|
||
|
s = 1 + x2 + y2,
|
||
|
sin3Eta = sqrt((s - sqrt(s * s - 4 * y * y)) / 2),
|
||
|
eta = asin(sin3Eta) / 3,
|
||
|
xi = sin3Eta ? arcosh(abs(y / sin3Eta)) / 3 : arsinh(abs(x)) / 3,
|
||
|
cosEta = cos(eta),
|
||
|
coshXi = cosh(xi),
|
||
|
d = coshXi * coshXi - cosEta * cosEta;
|
||
|
return [
|
||
|
sign(x) * 2 * atan2(sinh(xi) * cosEta, 0.25 - d),
|
||
|
sign(y) * 2 * atan2(coshXi * sin(eta), 0.25 + d)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function august() {
|
||
|
return d3Geo.geoProjection(augustRaw)
|
||
|
.scale(66.1603);
|
||
|
}
|
||
|
|
||
|
var sqrt8 = sqrt(8),
|
||
|
phi0 = log(1 + sqrt2);
|
||
|
|
||
|
function bakerRaw(lambda, phi) {
|
||
|
var phi0 = abs(phi);
|
||
|
return phi0 < quarterPi
|
||
|
? [lambda, log(tan(quarterPi + phi / 2))]
|
||
|
: [lambda * cos(phi0) * (2 * sqrt2 - 1 / sin(phi0)), sign(phi) * (2 * sqrt2 * (phi0 - quarterPi) - log(tan(phi0 / 2)))];
|
||
|
}
|
||
|
|
||
|
bakerRaw.invert = function(x, y) {
|
||
|
if ((y0 = abs(y)) < phi0) return [x, 2 * atan(exp(y)) - halfPi];
|
||
|
var phi = quarterPi, i = 25, delta, y0;
|
||
|
do {
|
||
|
var cosPhi_2 = cos(phi / 2), tanPhi_2 = tan(phi / 2);
|
||
|
phi -= delta = (sqrt8 * (phi - quarterPi) - log(tanPhi_2) - y0) / (sqrt8 - cosPhi_2 * cosPhi_2 / (2 * tanPhi_2));
|
||
|
} while (abs(delta) > epsilon2 && --i > 0);
|
||
|
return [x / (cos(phi) * (sqrt8 - 1 / sin(phi))), sign(y) * phi];
|
||
|
};
|
||
|
|
||
|
function baker() {
|
||
|
return d3Geo.geoProjection(bakerRaw)
|
||
|
.scale(112.314);
|
||
|
}
|
||
|
|
||
|
function berghausRaw(lobes) {
|
||
|
var k = 2 * pi / lobes;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var p = d3Geo.geoAzimuthalEquidistantRaw(lambda, phi);
|
||
|
if (abs(lambda) > halfPi) { // back hemisphere
|
||
|
var theta = atan2(p[1], p[0]),
|
||
|
r = sqrt(p[0] * p[0] + p[1] * p[1]),
|
||
|
theta0 = k * round((theta - halfPi) / k) + halfPi,
|
||
|
alpha = atan2(sin(theta -= theta0), 2 - cos(theta)); // angle relative to lobe end
|
||
|
theta = theta0 + asin(pi / r * sin(alpha)) - alpha;
|
||
|
p[0] = r * cos(theta);
|
||
|
p[1] = r * sin(theta);
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var r = sqrt(x * x + y * y);
|
||
|
if (r > halfPi) {
|
||
|
var theta = atan2(y, x),
|
||
|
theta0 = k * round((theta - halfPi) / k) + halfPi,
|
||
|
s = theta > theta0 ? -1 : 1,
|
||
|
A = r * cos(theta0 - theta),
|
||
|
cotAlpha = 1 / tan(s * acos((A - pi) / sqrt(pi * (pi - 2 * A) + r * r)));
|
||
|
theta = theta0 + 2 * atan((cotAlpha + s * sqrt(cotAlpha * cotAlpha - 3)) / 3);
|
||
|
x = r * cos(theta), y = r * sin(theta);
|
||
|
}
|
||
|
return d3Geo.geoAzimuthalEquidistantRaw.invert(x, y);
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function berghaus() {
|
||
|
var lobes = 5,
|
||
|
m = d3Geo.geoProjectionMutator(berghausRaw),
|
||
|
p = m(lobes),
|
||
|
projectionStream = p.stream,
|
||
|
epsilon$$1 = 1e-2,
|
||
|
cr = -cos(epsilon$$1 * radians),
|
||
|
sr = sin(epsilon$$1 * radians);
|
||
|
|
||
|
p.lobes = function(_) {
|
||
|
return arguments.length ? m(lobes = +_) : lobes;
|
||
|
};
|
||
|
|
||
|
p.stream = function(stream) {
|
||
|
var rotate = p.rotate(),
|
||
|
rotateStream = projectionStream(stream),
|
||
|
sphereStream = (p.rotate([0, 0]), projectionStream(stream));
|
||
|
p.rotate(rotate);
|
||
|
rotateStream.sphere = function() {
|
||
|
sphereStream.polygonStart(), sphereStream.lineStart();
|
||
|
for (var i = 0, delta = 360 / lobes, delta0 = 2 * pi / lobes, phi = 90 - 180 / lobes, phi0 = halfPi; i < lobes; ++i, phi -= delta, phi0 -= delta0) {
|
||
|
sphereStream.point(atan2(sr * cos(phi0), cr) * degrees, asin(sr * sin(phi0)) * degrees);
|
||
|
if (phi < -90) {
|
||
|
sphereStream.point(-90, -180 - phi - epsilon$$1);
|
||
|
sphereStream.point(-90, -180 - phi + epsilon$$1);
|
||
|
} else {
|
||
|
sphereStream.point(90, phi + epsilon$$1);
|
||
|
sphereStream.point(90, phi - epsilon$$1);
|
||
|
}
|
||
|
}
|
||
|
sphereStream.lineEnd(), sphereStream.polygonEnd();
|
||
|
};
|
||
|
return rotateStream;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(87.8076)
|
||
|
.center([0, 17.1875])
|
||
|
.clipAngle(180 - 1e-3);
|
||
|
}
|
||
|
|
||
|
function hammerRaw(A, B) {
|
||
|
if (arguments.length < 2) B = A;
|
||
|
if (B === 1) return d3Geo.geoAzimuthalEqualAreaRaw;
|
||
|
if (B === Infinity) return hammerQuarticAuthalicRaw;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var coordinates = d3Geo.geoAzimuthalEqualAreaRaw(lambda / B, phi);
|
||
|
coordinates[0] *= A;
|
||
|
return coordinates;
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var coordinates = d3Geo.geoAzimuthalEqualAreaRaw.invert(x / A, y);
|
||
|
coordinates[0] *= B;
|
||
|
return coordinates;
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function hammerQuarticAuthalicRaw(lambda, phi) {
|
||
|
return [
|
||
|
lambda * cos(phi) / cos(phi /= 2),
|
||
|
2 * sin(phi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
hammerQuarticAuthalicRaw.invert = function(x, y) {
|
||
|
var phi = 2 * asin(y / 2);
|
||
|
return [
|
||
|
x * cos(phi / 2) / cos(phi),
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function hammer() {
|
||
|
var B = 2,
|
||
|
m = d3Geo.geoProjectionMutator(hammerRaw),
|
||
|
p = m(B);
|
||
|
|
||
|
p.coefficient = function(_) {
|
||
|
if (!arguments.length) return B;
|
||
|
return m(B = +_);
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(169.529);
|
||
|
}
|
||
|
|
||
|
// Approximate Newton-Raphson
|
||
|
// Solve f(x) = y, start from x
|
||
|
function solve(f, y, x) {
|
||
|
var steps = 100, delta, f0, f1;
|
||
|
x = x === undefined ? 0 : +x;
|
||
|
y = +y;
|
||
|
do {
|
||
|
f0 = f(x);
|
||
|
f1 = f(x + epsilon);
|
||
|
if (f0 === f1) f1 = f0 + epsilon;
|
||
|
x -= delta = (-1 * epsilon * (f0 - y)) / (f0 - f1);
|
||
|
} while (steps-- > 0 && abs(delta) > epsilon);
|
||
|
return steps < 0 ? NaN : x;
|
||
|
}
|
||
|
|
||
|
// Approximate Newton-Raphson in 2D
|
||
|
// Solve f(a,b) = [x,y]
|
||
|
function solve2d(f, MAX_ITERATIONS, eps) {
|
||
|
if (MAX_ITERATIONS === undefined) MAX_ITERATIONS = 40;
|
||
|
if (eps === undefined) eps = epsilon2;
|
||
|
return function(x, y, a, b) {
|
||
|
var err2, da, db;
|
||
|
a = a === undefined ? 0 : +a;
|
||
|
b = b === undefined ? 0 : +b;
|
||
|
for (var i = 0; i < MAX_ITERATIONS; i++) {
|
||
|
var p = f(a, b),
|
||
|
// diffs
|
||
|
tx = p[0] - x,
|
||
|
ty = p[1] - y;
|
||
|
if (abs(tx) < eps && abs(ty) < eps) break; // we're there!
|
||
|
|
||
|
// backtrack if we overshot
|
||
|
var h = tx * tx + ty * ty;
|
||
|
if (h > err2) {
|
||
|
a -= da /= 2;
|
||
|
b -= db /= 2;
|
||
|
continue;
|
||
|
}
|
||
|
err2 = h;
|
||
|
|
||
|
// partial derivatives
|
||
|
var ea = (a > 0 ? -1 : 1) * eps,
|
||
|
eb = (b > 0 ? -1 : 1) * eps,
|
||
|
pa = f(a + ea, b),
|
||
|
pb = f(a, b + eb),
|
||
|
dxa = (pa[0] - p[0]) / ea,
|
||
|
dya = (pa[1] - p[1]) / ea,
|
||
|
dxb = (pb[0] - p[0]) / eb,
|
||
|
dyb = (pb[1] - p[1]) / eb,
|
||
|
// determinant
|
||
|
D = dyb * dxa - dya * dxb,
|
||
|
// newton step — or half-step for small D
|
||
|
l = (abs(D) < 0.5 ? 0.5 : 1) / D;
|
||
|
da = (ty * dxb - tx * dyb) * l;
|
||
|
db = (tx * dya - ty * dxa) * l;
|
||
|
a += da;
|
||
|
b += db;
|
||
|
if (abs(da) < eps && abs(db) < eps) break; // we're crawling
|
||
|
}
|
||
|
return [a, b];
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Bertin 1953 as a modified Briesemeister
|
||
|
// https://bl.ocks.org/Fil/5b9ee9636dfb6ffa53443c9006beb642
|
||
|
function bertin1953Raw() {
|
||
|
var hammer$$1 = hammerRaw(1.68, 2),
|
||
|
fu = 1.4, k = 12;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
|
||
|
if (lambda + phi < -fu) {
|
||
|
var u = (lambda - phi + 1.6) * (lambda + phi + fu) / 8;
|
||
|
lambda += u;
|
||
|
phi -= 0.8 * u * sin(phi + pi / 2);
|
||
|
}
|
||
|
|
||
|
var r = hammer$$1(lambda, phi);
|
||
|
|
||
|
var d = (1 - cos(lambda * phi)) / k;
|
||
|
|
||
|
if (r[1] < 0) {
|
||
|
r[0] *= 1 + d;
|
||
|
}
|
||
|
if (r[1] > 0) {
|
||
|
r[1] *= 1 + d / 1.5 * r[0] * r[0];
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
forward.invert = solve2d(forward);
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function bertin() {
|
||
|
// this projection should not be rotated
|
||
|
return d3Geo.geoProjection(bertin1953Raw())
|
||
|
.rotate([-16.5, -42])
|
||
|
.scale(176.57)
|
||
|
.center([7.93, 0.09]);
|
||
|
}
|
||
|
|
||
|
function mollweideBromleyTheta(cp, phi) {
|
||
|
var cpsinPhi = cp * sin(phi), i = 30, delta;
|
||
|
do phi -= delta = (phi + sin(phi) - cpsinPhi) / (1 + cos(phi));
|
||
|
while (abs(delta) > epsilon && --i > 0);
|
||
|
return phi / 2;
|
||
|
}
|
||
|
|
||
|
function mollweideBromleyRaw(cx, cy, cp) {
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
return [cx * lambda * cos(phi = mollweideBromleyTheta(cp, phi)), cy * sin(phi)];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
return y = asin(y / cy), [x / (cx * cos(y)), asin((2 * y + sin(2 * y)) / cp)];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
var mollweideRaw = mollweideBromleyRaw(sqrt2 / halfPi, sqrt2, pi);
|
||
|
|
||
|
function mollweide() {
|
||
|
return d3Geo.geoProjection(mollweideRaw)
|
||
|
.scale(169.529);
|
||
|
}
|
||
|
|
||
|
var k = 2.00276,
|
||
|
w = 1.11072;
|
||
|
|
||
|
function boggsRaw(lambda, phi) {
|
||
|
var theta = mollweideBromleyTheta(pi, phi);
|
||
|
return [k * lambda / (1 / cos(phi) + w / cos(theta)), (phi + sqrt2 * sin(theta)) / k];
|
||
|
}
|
||
|
|
||
|
boggsRaw.invert = function(x, y) {
|
||
|
var ky = k * y, theta = y < 0 ? -quarterPi : quarterPi, i = 25, delta, phi;
|
||
|
do {
|
||
|
phi = ky - sqrt2 * sin(theta);
|
||
|
theta -= delta = (sin(2 * theta) + 2 * theta - pi * sin(phi)) / (2 * cos(2 * theta) + 2 + pi * cos(phi) * sqrt2 * cos(theta));
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
phi = ky - sqrt2 * sin(theta);
|
||
|
return [x * (1 / cos(phi) + w / cos(theta)) / k, phi];
|
||
|
};
|
||
|
|
||
|
function boggs() {
|
||
|
return d3Geo.geoProjection(boggsRaw)
|
||
|
.scale(160.857);
|
||
|
}
|
||
|
|
||
|
function parallel1(projectAt) {
|
||
|
var phi0 = 0,
|
||
|
m = d3Geo.geoProjectionMutator(projectAt),
|
||
|
p = m(phi0);
|
||
|
|
||
|
p.parallel = function(_) {
|
||
|
return arguments.length ? m(phi0 = _ * radians) : phi0 * degrees;
|
||
|
};
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
function sinusoidalRaw(lambda, phi) {
|
||
|
return [lambda * cos(phi), phi];
|
||
|
}
|
||
|
|
||
|
sinusoidalRaw.invert = function(x, y) {
|
||
|
return [x / cos(y), y];
|
||
|
};
|
||
|
|
||
|
function sinusoidal() {
|
||
|
return d3Geo.geoProjection(sinusoidalRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function bonneRaw(phi0) {
|
||
|
if (!phi0) return sinusoidalRaw;
|
||
|
var cotPhi0 = 1 / tan(phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var rho = cotPhi0 + phi0 - phi,
|
||
|
e = rho ? lambda * cos(phi) / rho : rho;
|
||
|
return [rho * sin(e), cotPhi0 - rho * cos(e)];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var rho = sqrt(x * x + (y = cotPhi0 - y) * y),
|
||
|
phi = cotPhi0 + phi0 - rho;
|
||
|
return [rho / cos(phi) * atan2(x, y), phi];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function bonne() {
|
||
|
return parallel1(bonneRaw)
|
||
|
.scale(123.082)
|
||
|
.center([0, 26.1441])
|
||
|
.parallel(45);
|
||
|
}
|
||
|
|
||
|
function bottomleyRaw(sinPsi) {
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var rho = halfPi - phi,
|
||
|
eta = rho ? lambda * sinPsi * sin(rho) / rho : rho;
|
||
|
return [rho * sin(eta) / sinPsi, halfPi - rho * cos(eta)];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var x1 = x * sinPsi,
|
||
|
y1 = halfPi - y,
|
||
|
rho = sqrt(x1 * x1 + y1 * y1),
|
||
|
eta = atan2(x1, y1);
|
||
|
return [(rho ? rho / sin(rho) : 1) * eta / sinPsi, halfPi - rho];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function bottomley() {
|
||
|
var sinPsi = 0.5,
|
||
|
m = d3Geo.geoProjectionMutator(bottomleyRaw),
|
||
|
p = m(sinPsi);
|
||
|
|
||
|
p.fraction = function(_) {
|
||
|
return arguments.length ? m(sinPsi = +_) : sinPsi;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(158.837);
|
||
|
}
|
||
|
|
||
|
var bromleyRaw = mollweideBromleyRaw(1, 4 / pi, pi);
|
||
|
|
||
|
function bromley() {
|
||
|
return d3Geo.geoProjection(bromleyRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
// Azimuthal distance.
|
||
|
function distance(dPhi, c1, s1, c2, s2, dLambda) {
|
||
|
var cosdLambda = cos(dLambda), r;
|
||
|
if (abs(dPhi) > 1 || abs(dLambda) > 1) {
|
||
|
r = acos(s1 * s2 + c1 * c2 * cosdLambda);
|
||
|
} else {
|
||
|
var sindPhi = sin(dPhi / 2), sindLambda = sin(dLambda / 2);
|
||
|
r = 2 * asin(sqrt(sindPhi * sindPhi + c1 * c2 * sindLambda * sindLambda));
|
||
|
}
|
||
|
return abs(r) > epsilon ? [r, atan2(c2 * sin(dLambda), c1 * s2 - s1 * c2 * cosdLambda)] : [0, 0];
|
||
|
}
|
||
|
|
||
|
// Angle opposite a, and contained between sides of lengths b and c.
|
||
|
function angle(b, c, a) {
|
||
|
return acos((b * b + c * c - a * a) / (2 * b * c));
|
||
|
}
|
||
|
|
||
|
// Normalize longitude.
|
||
|
function longitude(lambda) {
|
||
|
return lambda - 2 * pi * floor((lambda + pi) / (2 * pi));
|
||
|
}
|
||
|
|
||
|
function chamberlinRaw(p0, p1, p2) {
|
||
|
var points = [
|
||
|
[p0[0], p0[1], sin(p0[1]), cos(p0[1])],
|
||
|
[p1[0], p1[1], sin(p1[1]), cos(p1[1])],
|
||
|
[p2[0], p2[1], sin(p2[1]), cos(p2[1])]
|
||
|
];
|
||
|
|
||
|
for (var a = points[2], b, i = 0; i < 3; ++i, a = b) {
|
||
|
b = points[i];
|
||
|
a.v = distance(b[1] - a[1], a[3], a[2], b[3], b[2], b[0] - a[0]);
|
||
|
a.point = [0, 0];
|
||
|
}
|
||
|
|
||
|
var beta0 = angle(points[0].v[0], points[2].v[0], points[1].v[0]),
|
||
|
beta1 = angle(points[0].v[0], points[1].v[0], points[2].v[0]),
|
||
|
beta2 = pi - beta0;
|
||
|
|
||
|
points[2].point[1] = 0;
|
||
|
points[0].point[0] = -(points[1].point[0] = points[0].v[0] / 2);
|
||
|
|
||
|
var mean = [
|
||
|
points[2].point[0] = points[0].point[0] + points[2].v[0] * cos(beta0),
|
||
|
2 * (points[0].point[1] = points[1].point[1] = points[2].v[0] * sin(beta0))
|
||
|
];
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var sinPhi = sin(phi),
|
||
|
cosPhi = cos(phi),
|
||
|
v = new Array(3), i;
|
||
|
|
||
|
// Compute distance and azimuth from control points.
|
||
|
for (i = 0; i < 3; ++i) {
|
||
|
var p = points[i];
|
||
|
v[i] = distance(phi - p[1], p[3], p[2], cosPhi, sinPhi, lambda - p[0]);
|
||
|
if (!v[i][0]) return p.point;
|
||
|
v[i][1] = longitude(v[i][1] - p.v[1]);
|
||
|
}
|
||
|
|
||
|
// Arithmetic mean of interception points.
|
||
|
var point = mean.slice();
|
||
|
for (i = 0; i < 3; ++i) {
|
||
|
var j = i == 2 ? 0 : i + 1;
|
||
|
var a = angle(points[i].v[0], v[i][0], v[j][0]);
|
||
|
if (v[i][1] < 0) a = -a;
|
||
|
|
||
|
if (!i) {
|
||
|
point[0] += v[i][0] * cos(a);
|
||
|
point[1] -= v[i][0] * sin(a);
|
||
|
} else if (i == 1) {
|
||
|
a = beta1 - a;
|
||
|
point[0] -= v[i][0] * cos(a);
|
||
|
point[1] -= v[i][0] * sin(a);
|
||
|
} else {
|
||
|
a = beta2 - a;
|
||
|
point[0] += v[i][0] * cos(a);
|
||
|
point[1] += v[i][0] * sin(a);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
point[0] /= 3, point[1] /= 3;
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function pointRadians(p) {
|
||
|
return p[0] *= radians, p[1] *= radians, p;
|
||
|
}
|
||
|
|
||
|
function chamberlinAfrica() {
|
||
|
return chamberlin([0, 22], [45, 22], [22.5, -22])
|
||
|
.scale(380)
|
||
|
.center([22.5, 2]);
|
||
|
}
|
||
|
|
||
|
function chamberlin(p0, p1, p2) { // TODO order matters!
|
||
|
var c = d3Geo.geoCentroid({type: "MultiPoint", coordinates: [p0, p1, p2]}),
|
||
|
R = [-c[0], -c[1]],
|
||
|
r = d3Geo.geoRotation(R),
|
||
|
f = chamberlinRaw(pointRadians(r(p0)), pointRadians(r(p1)), pointRadians(r(p2)));
|
||
|
f.invert = solve2d(f);
|
||
|
var p = d3Geo.geoProjection(f).rotate(R),
|
||
|
center = p.center;
|
||
|
|
||
|
delete p.rotate;
|
||
|
|
||
|
p.center = function(_) {
|
||
|
return arguments.length ? center(r(_)) : r.invert(center());
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.clipAngle(90);
|
||
|
}
|
||
|
|
||
|
function collignonRaw(lambda, phi) {
|
||
|
var alpha = sqrt(1 - sin(phi));
|
||
|
return [(2 / sqrtPi) * lambda * alpha, sqrtPi * (1 - alpha)];
|
||
|
}
|
||
|
|
||
|
collignonRaw.invert = function(x, y) {
|
||
|
var lambda = (lambda = y / sqrtPi - 1) * lambda;
|
||
|
return [lambda > 0 ? x * sqrt(pi / lambda) / 2 : 0, asin(1 - lambda)];
|
||
|
};
|
||
|
|
||
|
function collignon() {
|
||
|
return d3Geo.geoProjection(collignonRaw)
|
||
|
.scale(95.6464)
|
||
|
.center([0, 30]);
|
||
|
}
|
||
|
|
||
|
function craigRaw(phi0) {
|
||
|
var tanPhi0 = tan(phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
return [lambda, (lambda ? lambda / sin(lambda) : 1) * (sin(phi) * cos(lambda) - tanPhi0 * cos(phi))];
|
||
|
}
|
||
|
|
||
|
forward.invert = tanPhi0 ? function(x, y) {
|
||
|
if (x) y *= sin(x) / x;
|
||
|
var cosLambda = cos(x);
|
||
|
return [x, 2 * atan2(sqrt(cosLambda * cosLambda + tanPhi0 * tanPhi0 - y * y) - cosLambda, tanPhi0 - y)];
|
||
|
} : function(x, y) {
|
||
|
return [x, asin(x ? y * tan(x) / x : y)];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function craig() {
|
||
|
return parallel1(craigRaw)
|
||
|
.scale(249.828)
|
||
|
.clipAngle(90);
|
||
|
}
|
||
|
|
||
|
var sqrt3 = sqrt(3);
|
||
|
|
||
|
function crasterRaw(lambda, phi) {
|
||
|
return [sqrt3 * lambda * (2 * cos(2 * phi / 3) - 1) / sqrtPi, sqrt3 * sqrtPi * sin(phi / 3)];
|
||
|
}
|
||
|
|
||
|
crasterRaw.invert = function(x, y) {
|
||
|
var phi = 3 * asin(y / (sqrt3 * sqrtPi));
|
||
|
return [sqrtPi * x / (sqrt3 * (2 * cos(2 * phi / 3) - 1)), phi];
|
||
|
};
|
||
|
|
||
|
function craster() {
|
||
|
return d3Geo.geoProjection(crasterRaw)
|
||
|
.scale(156.19);
|
||
|
}
|
||
|
|
||
|
function cylindricalEqualAreaRaw(phi0) {
|
||
|
var cosPhi0 = cos(phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
return [lambda * cosPhi0, sin(phi) / cosPhi0];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
return [x / cosPhi0, asin(y * cosPhi0)];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function cylindricalEqualArea() {
|
||
|
return parallel1(cylindricalEqualAreaRaw)
|
||
|
.parallel(38.58) // acos(sqrt(width / height / pi)) * radians
|
||
|
.scale(195.044); // width / (sqrt(width / height / pi) * 2 * pi)
|
||
|
}
|
||
|
|
||
|
function cylindricalStereographicRaw(phi0) {
|
||
|
var cosPhi0 = cos(phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
return [lambda * cosPhi0, (1 + cosPhi0) * tan(phi / 2)];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
return [x / cosPhi0, atan(y / (1 + cosPhi0)) * 2];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function cylindricalStereographic() {
|
||
|
return parallel1(cylindricalStereographicRaw)
|
||
|
.scale(124.75);
|
||
|
}
|
||
|
|
||
|
function eckert1Raw(lambda, phi) {
|
||
|
var alpha = sqrt(8 / (3 * pi));
|
||
|
return [
|
||
|
alpha * lambda * (1 - abs(phi) / pi),
|
||
|
alpha * phi
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert1Raw.invert = function(x, y) {
|
||
|
var alpha = sqrt(8 / (3 * pi)),
|
||
|
phi = y / alpha;
|
||
|
return [
|
||
|
x / (alpha * (1 - abs(phi) / pi)),
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert1() {
|
||
|
return d3Geo.geoProjection(eckert1Raw)
|
||
|
.scale(165.664);
|
||
|
}
|
||
|
|
||
|
function eckert2Raw(lambda, phi) {
|
||
|
var alpha = sqrt(4 - 3 * sin(abs(phi)));
|
||
|
return [
|
||
|
2 / sqrt(6 * pi) * lambda * alpha,
|
||
|
sign(phi) * sqrt(2 * pi / 3) * (2 - alpha)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert2Raw.invert = function(x, y) {
|
||
|
var alpha = 2 - abs(y) / sqrt(2 * pi / 3);
|
||
|
return [
|
||
|
x * sqrt(6 * pi) / (2 * alpha),
|
||
|
sign(y) * asin((4 - alpha * alpha) / 3)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert2() {
|
||
|
return d3Geo.geoProjection(eckert2Raw)
|
||
|
.scale(165.664);
|
||
|
}
|
||
|
|
||
|
function eckert3Raw(lambda, phi) {
|
||
|
var k = sqrt(pi * (4 + pi));
|
||
|
return [
|
||
|
2 / k * lambda * (1 + sqrt(1 - 4 * phi * phi / (pi * pi))),
|
||
|
4 / k * phi
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert3Raw.invert = function(x, y) {
|
||
|
var k = sqrt(pi * (4 + pi)) / 2;
|
||
|
return [
|
||
|
x * k / (1 + sqrt(1 - y * y * (4 + pi) / (4 * pi))),
|
||
|
y * k / 2
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert3() {
|
||
|
return d3Geo.geoProjection(eckert3Raw)
|
||
|
.scale(180.739);
|
||
|
}
|
||
|
|
||
|
function eckert4Raw(lambda, phi) {
|
||
|
var k = (2 + halfPi) * sin(phi);
|
||
|
phi /= 2;
|
||
|
for (var i = 0, delta = Infinity; i < 10 && abs(delta) > epsilon; i++) {
|
||
|
var cosPhi = cos(phi);
|
||
|
phi -= delta = (phi + sin(phi) * (cosPhi + 2) - k) / (2 * cosPhi * (1 + cosPhi));
|
||
|
}
|
||
|
return [
|
||
|
2 / sqrt(pi * (4 + pi)) * lambda * (1 + cos(phi)),
|
||
|
2 * sqrt(pi / (4 + pi)) * sin(phi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert4Raw.invert = function(x, y) {
|
||
|
var A = y * sqrt((4 + pi) / pi) / 2,
|
||
|
k = asin(A),
|
||
|
c = cos(k);
|
||
|
return [
|
||
|
x / (2 / sqrt(pi * (4 + pi)) * (1 + c)),
|
||
|
asin((k + A * (c + 2)) / (2 + halfPi))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert4() {
|
||
|
return d3Geo.geoProjection(eckert4Raw)
|
||
|
.scale(180.739);
|
||
|
}
|
||
|
|
||
|
function eckert5Raw(lambda, phi) {
|
||
|
return [
|
||
|
lambda * (1 + cos(phi)) / sqrt(2 + pi),
|
||
|
2 * phi / sqrt(2 + pi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert5Raw.invert = function(x, y) {
|
||
|
var k = sqrt(2 + pi),
|
||
|
phi = y * k / 2;
|
||
|
return [
|
||
|
k * x / (1 + cos(phi)),
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert5() {
|
||
|
return d3Geo.geoProjection(eckert5Raw)
|
||
|
.scale(173.044);
|
||
|
}
|
||
|
|
||
|
function eckert6Raw(lambda, phi) {
|
||
|
var k = (1 + halfPi) * sin(phi);
|
||
|
for (var i = 0, delta = Infinity; i < 10 && abs(delta) > epsilon; i++) {
|
||
|
phi -= delta = (phi + sin(phi) - k) / (1 + cos(phi));
|
||
|
}
|
||
|
k = sqrt(2 + pi);
|
||
|
return [
|
||
|
lambda * (1 + cos(phi)) / k,
|
||
|
2 * phi / k
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eckert6Raw.invert = function(x, y) {
|
||
|
var j = 1 + halfPi,
|
||
|
k = sqrt(j / 2);
|
||
|
return [
|
||
|
x * 2 * k / (1 + cos(y *= k)),
|
||
|
asin((y + sin(y)) / j)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function eckert6() {
|
||
|
return d3Geo.geoProjection(eckert6Raw)
|
||
|
.scale(173.044);
|
||
|
}
|
||
|
|
||
|
var eisenlohrK = 3 + 2 * sqrt2;
|
||
|
|
||
|
function eisenlohrRaw(lambda, phi) {
|
||
|
var s0 = sin(lambda /= 2),
|
||
|
c0 = cos(lambda),
|
||
|
k = sqrt(cos(phi)),
|
||
|
c1 = cos(phi /= 2),
|
||
|
t = sin(phi) / (c1 + sqrt2 * c0 * k),
|
||
|
c = sqrt(2 / (1 + t * t)),
|
||
|
v = sqrt((sqrt2 * c1 + (c0 + s0) * k) / (sqrt2 * c1 + (c0 - s0) * k));
|
||
|
return [
|
||
|
eisenlohrK * (c * (v - 1 / v) - 2 * log(v)),
|
||
|
eisenlohrK * (c * t * (v + 1 / v) - 2 * atan(t))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
eisenlohrRaw.invert = function(x, y) {
|
||
|
if (!(p = augustRaw.invert(x / 1.2, y * 1.065))) return null;
|
||
|
var lambda = p[0], phi = p[1], i = 20, p;
|
||
|
x /= eisenlohrK, y /= eisenlohrK;
|
||
|
do {
|
||
|
var _0 = lambda / 2,
|
||
|
_1 = phi / 2,
|
||
|
s0 = sin(_0),
|
||
|
c0 = cos(_0),
|
||
|
s1 = sin(_1),
|
||
|
c1 = cos(_1),
|
||
|
cos1 = cos(phi),
|
||
|
k = sqrt(cos1),
|
||
|
t = s1 / (c1 + sqrt2 * c0 * k),
|
||
|
t2 = t * t,
|
||
|
c = sqrt(2 / (1 + t2)),
|
||
|
v0 = (sqrt2 * c1 + (c0 + s0) * k),
|
||
|
v1 = (sqrt2 * c1 + (c0 - s0) * k),
|
||
|
v2 = v0 / v1,
|
||
|
v = sqrt(v2),
|
||
|
vm1v = v - 1 / v,
|
||
|
vp1v = v + 1 / v,
|
||
|
fx = c * vm1v - 2 * log(v) - x,
|
||
|
fy = c * t * vp1v - 2 * atan(t) - y,
|
||
|
deltatDeltaLambda = s1 && sqrt1_2 * k * s0 * t2 / s1,
|
||
|
deltatDeltaPhi = (sqrt2 * c0 * c1 + k) / (2 * (c1 + sqrt2 * c0 * k) * (c1 + sqrt2 * c0 * k) * k),
|
||
|
deltacDeltat = -0.5 * t * c * c * c,
|
||
|
deltacDeltaLambda = deltacDeltat * deltatDeltaLambda,
|
||
|
deltacDeltaPhi = deltacDeltat * deltatDeltaPhi,
|
||
|
A = (A = 2 * c1 + sqrt2 * k * (c0 - s0)) * A * v,
|
||
|
deltavDeltaLambda = (sqrt2 * c0 * c1 * k + cos1) / A,
|
||
|
deltavDeltaPhi = -(sqrt2 * s0 * s1) / (k * A),
|
||
|
deltaxDeltaLambda = vm1v * deltacDeltaLambda - 2 * deltavDeltaLambda / v + c * (deltavDeltaLambda + deltavDeltaLambda / v2),
|
||
|
deltaxDeltaPhi = vm1v * deltacDeltaPhi - 2 * deltavDeltaPhi / v + c * (deltavDeltaPhi + deltavDeltaPhi / v2),
|
||
|
deltayDeltaLambda = t * vp1v * deltacDeltaLambda - 2 * deltatDeltaLambda / (1 + t2) + c * vp1v * deltatDeltaLambda + c * t * (deltavDeltaLambda - deltavDeltaLambda / v2),
|
||
|
deltayDeltaPhi = t * vp1v * deltacDeltaPhi - 2 * deltatDeltaPhi / (1 + t2) + c * vp1v * deltatDeltaPhi + c * t * (deltavDeltaPhi - deltavDeltaPhi / v2),
|
||
|
denominator = deltaxDeltaPhi * deltayDeltaLambda - deltayDeltaPhi * deltaxDeltaLambda;
|
||
|
if (!denominator) break;
|
||
|
var deltaLambda = (fy * deltaxDeltaPhi - fx * deltayDeltaPhi) / denominator,
|
||
|
deltaPhi = (fx * deltayDeltaLambda - fy * deltaxDeltaLambda) / denominator;
|
||
|
lambda -= deltaLambda;
|
||
|
phi = max(-halfPi, min(halfPi, phi - deltaPhi));
|
||
|
} while ((abs(deltaLambda) > epsilon || abs(deltaPhi) > epsilon) && --i > 0);
|
||
|
return abs(abs(phi) - halfPi) < epsilon ? [0, phi] : i && [lambda, phi];
|
||
|
};
|
||
|
|
||
|
function eisenlohr() {
|
||
|
return d3Geo.geoProjection(eisenlohrRaw)
|
||
|
.scale(62.5271);
|
||
|
}
|
||
|
|
||
|
var faheyK = cos(35 * radians);
|
||
|
|
||
|
function faheyRaw(lambda, phi) {
|
||
|
var t = tan(phi / 2);
|
||
|
return [lambda * faheyK * sqrt(1 - t * t), (1 + faheyK) * t];
|
||
|
}
|
||
|
|
||
|
faheyRaw.invert = function(x, y) {
|
||
|
var t = y / (1 + faheyK);
|
||
|
return [x && x / (faheyK * sqrt(1 - t * t)), 2 * atan(t)];
|
||
|
};
|
||
|
|
||
|
function fahey() {
|
||
|
return d3Geo.geoProjection(faheyRaw)
|
||
|
.scale(137.152);
|
||
|
}
|
||
|
|
||
|
function foucautRaw(lambda, phi) {
|
||
|
var k = phi / 2, cosk = cos(k);
|
||
|
return [ 2 * lambda / sqrtPi * cos(phi) * cosk * cosk, sqrtPi * tan(k)];
|
||
|
}
|
||
|
|
||
|
foucautRaw.invert = function(x, y) {
|
||
|
var k = atan(y / sqrtPi), cosk = cos(k), phi = 2 * k;
|
||
|
return [x * sqrtPi / 2 / (cos(phi) * cosk * cosk), phi];
|
||
|
};
|
||
|
|
||
|
function foucaut() {
|
||
|
return d3Geo.geoProjection(foucautRaw)
|
||
|
.scale(135.264);
|
||
|
}
|
||
|
|
||
|
function foucautSinusoidalRaw(alpha) {
|
||
|
var beta = 1 - alpha,
|
||
|
equatorial = raw(pi, 0)[0] - raw(-pi, 0)[0],
|
||
|
polar = raw(0, halfPi)[1] - raw(0, -halfPi)[1],
|
||
|
ratio = sqrt(2 * polar / equatorial);
|
||
|
|
||
|
function raw(lambda, phi) {
|
||
|
var cosphi = cos(phi),
|
||
|
sinphi = sin(phi);
|
||
|
return [
|
||
|
cosphi / (beta + alpha * cosphi) * lambda,
|
||
|
beta * phi + alpha * sinphi
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var p = raw(lambda, phi);
|
||
|
return [p[0] * ratio, p[1] / ratio];
|
||
|
}
|
||
|
|
||
|
function forwardMeridian(phi) {
|
||
|
return forward(0, phi)[1];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var phi = solve(forwardMeridian, y),
|
||
|
lambda = x / ratio * (alpha + beta / cos(phi));
|
||
|
return [lambda, phi];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function foucautSinusoidal() {
|
||
|
var alpha = 0.5,
|
||
|
m = d3Geo.geoProjectionMutator(foucautSinusoidalRaw),
|
||
|
p = m(alpha);
|
||
|
|
||
|
p.alpha = function(_) {
|
||
|
return arguments.length ? m(alpha = +_) : alpha;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(168.725);
|
||
|
}
|
||
|
|
||
|
function gilbertForward(point) {
|
||
|
return [point[0] / 2, asin(tan(point[1] / 2 * radians)) * degrees];
|
||
|
}
|
||
|
|
||
|
function gilbertInvert(point) {
|
||
|
return [point[0] * 2, 2 * atan(sin(point[1] * radians)) * degrees];
|
||
|
}
|
||
|
|
||
|
function gilbert(projectionType) {
|
||
|
if (projectionType == null) projectionType = d3Geo.geoOrthographic;
|
||
|
var projection = projectionType(),
|
||
|
equirectangular = d3Geo.geoEquirectangular().scale(degrees).precision(0).clipAngle(null).translate([0, 0]); // antimeridian cutting
|
||
|
|
||
|
function gilbert(point) {
|
||
|
return projection(gilbertForward(point));
|
||
|
}
|
||
|
|
||
|
if (projection.invert) gilbert.invert = function(point) {
|
||
|
return gilbertInvert(projection.invert(point));
|
||
|
};
|
||
|
|
||
|
gilbert.stream = function(stream) {
|
||
|
var s1 = projection.stream(stream), s0 = equirectangular.stream({
|
||
|
point: function(lambda, phi) { s1.point(lambda / 2, asin(tan(-phi / 2 * radians)) * degrees); },
|
||
|
lineStart: function() { s1.lineStart(); },
|
||
|
lineEnd: function() { s1.lineEnd(); },
|
||
|
polygonStart: function() { s1.polygonStart(); },
|
||
|
polygonEnd: function() { s1.polygonEnd(); }
|
||
|
});
|
||
|
s0.sphere = s1.sphere;
|
||
|
return s0;
|
||
|
};
|
||
|
|
||
|
function property(name) {
|
||
|
gilbert[name] = function() {
|
||
|
return arguments.length ? (projection[name].apply(projection, arguments), gilbert) : projection[name]();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
gilbert.rotate = function(_) {
|
||
|
return arguments.length ? (equirectangular.rotate(_), gilbert) : equirectangular.rotate();
|
||
|
};
|
||
|
|
||
|
gilbert.center = function(_) {
|
||
|
return arguments.length ? (projection.center(gilbertForward(_)), gilbert) : gilbertInvert(projection.center());
|
||
|
};
|
||
|
|
||
|
property("angle");
|
||
|
property("clipAngle");
|
||
|
property("clipExtent");
|
||
|
property("fitExtent");
|
||
|
property("fitHeight");
|
||
|
property("fitSize");
|
||
|
property("fitWidth");
|
||
|
property("scale");
|
||
|
property("translate");
|
||
|
property("precision");
|
||
|
|
||
|
return gilbert
|
||
|
.scale(249.5);
|
||
|
}
|
||
|
|
||
|
function gingeryRaw(rho, n) {
|
||
|
var k = 2 * pi / n,
|
||
|
rho2 = rho * rho;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var p = d3Geo.geoAzimuthalEquidistantRaw(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 d3Geo.geoAzimuthalEquidistantRaw.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;
|
||
|
}
|
||
|
|
||
|
function gingery() {
|
||
|
var n = 6,
|
||
|
rho = 30 * radians,
|
||
|
cRho = cos(rho),
|
||
|
sRho = sin(rho),
|
||
|
m = d3Geo.geoProjectionMutator(gingeryRaw),
|
||
|
p = m(rho, n),
|
||
|
stream_ = p.stream,
|
||
|
epsilon$$1 = 1e-2,
|
||
|
cr = -cos(epsilon$$1 * radians),
|
||
|
sr = sin(epsilon$$1 * 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);
|
||
|
}
|
||
|
|
||
|
function ginzburgPolyconicRaw(a, b, c, d, e, f, g, h) {
|
||
|
if (arguments.length < 8) h = 0;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
if (!phi) return [a * lambda / pi, 0];
|
||
|
var phi2 = phi * phi,
|
||
|
xB = a + phi2 * (b + phi2 * (c + phi2 * d)),
|
||
|
yB = phi * (e - 1 + phi2 * (f - h + phi2 * g)),
|
||
|
m = (xB * xB + yB * yB) / (2 * yB),
|
||
|
alpha = lambda * asin(xB / m) / pi;
|
||
|
return [m * sin(alpha), phi * (1 + phi2 * h) + m * (1 - cos(alpha))];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var lambda = pi * x / a,
|
||
|
phi = y,
|
||
|
deltaLambda, deltaPhi, i = 50;
|
||
|
do {
|
||
|
var phi2 = phi * phi,
|
||
|
xB = a + phi2 * (b + phi2 * (c + phi2 * d)),
|
||
|
yB = phi * (e - 1 + phi2 * (f - h + phi2 * g)),
|
||
|
p = xB * xB + yB * yB,
|
||
|
q = 2 * yB,
|
||
|
m = p / q,
|
||
|
m2 = m * m,
|
||
|
dAlphadLambda = asin(xB / m) / pi,
|
||
|
alpha = lambda * dAlphadLambda,
|
||
|
xB2 = xB * xB,
|
||
|
dxBdPhi = (2 * b + phi2 * (4 * c + phi2 * 6 * d)) * phi,
|
||
|
dyBdPhi = e + phi2 * (3 * f + phi2 * 5 * g),
|
||
|
dpdPhi = 2 * (xB * dxBdPhi + yB * (dyBdPhi - 1)),
|
||
|
dqdPhi = 2 * (dyBdPhi - 1),
|
||
|
dmdPhi = (dpdPhi * q - p * dqdPhi) / (q * q),
|
||
|
cosAlpha = cos(alpha),
|
||
|
sinAlpha = sin(alpha),
|
||
|
mcosAlpha = m * cosAlpha,
|
||
|
msinAlpha = m * sinAlpha,
|
||
|
dAlphadPhi = ((lambda / pi) * (1 / sqrt(1 - xB2 / m2)) * (dxBdPhi * m - xB * dmdPhi)) / m2,
|
||
|
fx = msinAlpha - x,
|
||
|
fy = phi * (1 + phi2 * h) + m - mcosAlpha - y,
|
||
|
deltaxDeltaPhi = dmdPhi * sinAlpha + mcosAlpha * dAlphadPhi,
|
||
|
deltaxDeltaLambda = mcosAlpha * dAlphadLambda,
|
||
|
deltayDeltaPhi = 1 + dmdPhi - (dmdPhi * cosAlpha - msinAlpha * dAlphadPhi),
|
||
|
deltayDeltaLambda = msinAlpha * dAlphadLambda,
|
||
|
denominator = deltaxDeltaPhi * deltayDeltaLambda - deltayDeltaPhi * deltaxDeltaLambda;
|
||
|
if (!denominator) break;
|
||
|
lambda -= deltaLambda = (fy * deltaxDeltaPhi - fx * deltayDeltaPhi) / denominator;
|
||
|
phi -= deltaPhi = (fx * deltayDeltaLambda - fy * deltaxDeltaLambda) / denominator;
|
||
|
} while ((abs(deltaLambda) > epsilon || abs(deltaPhi) > epsilon) && --i > 0);
|
||
|
return [lambda, phi];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
var ginzburg4Raw = ginzburgPolyconicRaw(2.8284, -1.6988, 0.75432, -0.18071, 1.76003, -0.38914, 0.042555);
|
||
|
|
||
|
function ginzburg4() {
|
||
|
return d3Geo.geoProjection(ginzburg4Raw)
|
||
|
.scale(149.995);
|
||
|
}
|
||
|
|
||
|
var ginzburg5Raw = ginzburgPolyconicRaw(2.583819, -0.835827, 0.170354, -0.038094, 1.543313, -0.411435,0.082742);
|
||
|
|
||
|
function ginzburg5() {
|
||
|
return d3Geo.geoProjection(ginzburg5Raw)
|
||
|
.scale(153.93);
|
||
|
}
|
||
|
|
||
|
var ginzburg6Raw = ginzburgPolyconicRaw(5 / 6 * pi, -0.62636, -0.0344, 0, 1.3493, -0.05524, 0, 0.045);
|
||
|
|
||
|
function ginzburg6() {
|
||
|
return d3Geo.geoProjection(ginzburg6Raw)
|
||
|
.scale(130.945);
|
||
|
}
|
||
|
|
||
|
function ginzburg8Raw(lambda, phi) {
|
||
|
var lambda2 = lambda * lambda,
|
||
|
phi2 = phi * phi;
|
||
|
return [
|
||
|
lambda * (1 - 0.162388 * phi2) * (0.87 - 0.000952426 * lambda2 * lambda2),
|
||
|
phi * (1 + phi2 / 12)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
ginzburg8Raw.invert = function(x, y) {
|
||
|
var lambda = x,
|
||
|
phi = y,
|
||
|
i = 50, delta;
|
||
|
do {
|
||
|
var phi2 = phi * phi;
|
||
|
phi -= delta = (phi * (1 + phi2 / 12) - y) / (1 + phi2 / 4);
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
i = 50;
|
||
|
x /= 1 -0.162388 * phi2;
|
||
|
do {
|
||
|
var lambda4 = (lambda4 = lambda * lambda) * lambda4;
|
||
|
lambda -= delta = (lambda * (0.87 - 0.000952426 * lambda4) - x) / (0.87 - 0.00476213 * lambda4);
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
return [lambda, phi];
|
||
|
};
|
||
|
|
||
|
function ginzburg8() {
|
||
|
return d3Geo.geoProjection(ginzburg8Raw)
|
||
|
.scale(131.747);
|
||
|
}
|
||
|
|
||
|
var ginzburg9Raw = ginzburgPolyconicRaw(2.6516, -0.76534, 0.19123, -0.047094, 1.36289, -0.13965,0.031762);
|
||
|
|
||
|
function ginzburg9() {
|
||
|
return d3Geo.geoProjection(ginzburg9Raw)
|
||
|
.scale(131.087);
|
||
|
}
|
||
|
|
||
|
function squareRaw(project) {
|
||
|
var dx = project(halfPi, 0)[0] - project(-halfPi, 0)[0];
|
||
|
|
||
|
function projectSquare(lambda, phi) {
|
||
|
var s = lambda > 0 ? -0.5 : 0.5,
|
||
|
point = project(lambda + s * pi, phi);
|
||
|
point[0] -= s * dx;
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
if (project.invert) projectSquare.invert = function(x, y) {
|
||
|
var s = x > 0 ? -0.5 : 0.5,
|
||
|
location = project.invert(x + s * dx, y),
|
||
|
lambda = location[0] - s * pi;
|
||
|
if (lambda < -pi) lambda += 2 * pi;
|
||
|
else if (lambda > pi) lambda -= 2 * pi;
|
||
|
location[0] = lambda;
|
||
|
return location;
|
||
|
};
|
||
|
|
||
|
return projectSquare;
|
||
|
}
|
||
|
|
||
|
function gringortenRaw(lambda, phi) {
|
||
|
var sLambda = sign(lambda),
|
||
|
sPhi = sign(phi),
|
||
|
cosPhi = cos(phi),
|
||
|
x = cos(lambda) * cosPhi,
|
||
|
y = sin(lambda) * cosPhi,
|
||
|
z = sin(sPhi * phi);
|
||
|
lambda = abs(atan2(y, z));
|
||
|
phi = asin(x);
|
||
|
if (abs(lambda - halfPi) > epsilon) lambda %= halfPi;
|
||
|
var point = gringortenHexadecant(lambda > pi / 4 ? halfPi - lambda : lambda, phi);
|
||
|
if (lambda > pi / 4) z = point[0], point[0] = -point[1], point[1] = -z;
|
||
|
return (point[0] *= sLambda, point[1] *= -sPhi, point);
|
||
|
}
|
||
|
|
||
|
gringortenRaw.invert = function(x, y) {
|
||
|
if (abs(x) > 1) x = sign(x) * 2 - x;
|
||
|
if (abs(y) > 1) y = sign(y) * 2 - y;
|
||
|
var sx = sign(x),
|
||
|
sy = sign(y),
|
||
|
x0 = -sx * x,
|
||
|
y0 = -sy * y,
|
||
|
t = y0 / x0 < 1,
|
||
|
p = gringortenHexadecantInvert(t ? y0 : x0, t ? x0 : y0),
|
||
|
lambda = p[0],
|
||
|
phi = p[1],
|
||
|
cosPhi = cos(phi);
|
||
|
if (t) lambda = -halfPi - lambda;
|
||
|
return [sx * (atan2(sin(lambda) * cosPhi, -sin(phi)) + pi), sy * asin(cos(lambda) * cosPhi)];
|
||
|
};
|
||
|
|
||
|
function gringortenHexadecant(lambda, phi) {
|
||
|
if (phi === halfPi) return [0, 0];
|
||
|
|
||
|
var sinPhi = sin(phi),
|
||
|
r = sinPhi * sinPhi,
|
||
|
r2 = r * r,
|
||
|
j = 1 + r2,
|
||
|
k = 1 + 3 * r2,
|
||
|
q = 1 - r2,
|
||
|
z = asin(1 / sqrt(j)),
|
||
|
v = q + r * j * z,
|
||
|
p2 = (1 - sinPhi) / v,
|
||
|
p = sqrt(p2),
|
||
|
a2 = p2 * j,
|
||
|
a = sqrt(a2),
|
||
|
h = p * q,
|
||
|
x,
|
||
|
i;
|
||
|
|
||
|
if (lambda === 0) return [0, -(h + r * a)];
|
||
|
|
||
|
var cosPhi = cos(phi),
|
||
|
secPhi = 1 / cosPhi,
|
||
|
drdPhi = 2 * sinPhi * cosPhi,
|
||
|
dvdPhi = (-3 * r + z * k) * drdPhi,
|
||
|
dp2dPhi = (-v * cosPhi - (1 - sinPhi) * dvdPhi) / (v * v),
|
||
|
dpdPhi = (0.5 * dp2dPhi) / p,
|
||
|
dhdPhi = q * dpdPhi - 2 * r * p * drdPhi,
|
||
|
dra2dPhi = r * j * dp2dPhi + p2 * k * drdPhi,
|
||
|
mu = -secPhi * drdPhi,
|
||
|
nu = -secPhi * dra2dPhi,
|
||
|
zeta = -2 * secPhi * dhdPhi,
|
||
|
lambda1 = 4 * lambda / pi,
|
||
|
delta;
|
||
|
|
||
|
// Slower but accurate bisection method.
|
||
|
if (lambda > 0.222 * pi || phi < pi / 4 && lambda > 0.175 * pi) {
|
||
|
x = (h + r * sqrt(a2 * (1 + r2) - h * h)) / (1 + r2);
|
||
|
if (lambda > pi / 4) return [x, x];
|
||
|
var x1 = x, x0 = 0.5 * x;
|
||
|
x = 0.5 * (x0 + x1), i = 50;
|
||
|
do {
|
||
|
var g = sqrt(a2 - x * x),
|
||
|
f = (x * (zeta + mu * g) + nu * asin(x / a)) - lambda1;
|
||
|
if (!f) break;
|
||
|
if (f < 0) x0 = x;
|
||
|
else x1 = x;
|
||
|
x = 0.5 * (x0 + x1);
|
||
|
} while (abs(x1 - x0) > epsilon && --i > 0);
|
||
|
}
|
||
|
|
||
|
// Newton-Raphson.
|
||
|
else {
|
||
|
x = epsilon, i = 25;
|
||
|
do {
|
||
|
var x2 = x * x,
|
||
|
g2 = sqrt(a2 - x2),
|
||
|
zetaMug = zeta + mu * g2,
|
||
|
f2 = x * zetaMug + nu * asin(x / a) - lambda1,
|
||
|
df = zetaMug + (nu - mu * x2) / g2;
|
||
|
x -= delta = g2 ? f2 / df : 0;
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
}
|
||
|
|
||
|
return [x, -h - r * sqrt(a2 - x * x)];
|
||
|
}
|
||
|
|
||
|
function gringortenHexadecantInvert(x, y) {
|
||
|
var x0 = 0,
|
||
|
x1 = 1,
|
||
|
r = 0.5,
|
||
|
i = 50;
|
||
|
|
||
|
while (true) {
|
||
|
var r2 = r * r,
|
||
|
sinPhi = sqrt(r),
|
||
|
z = asin(1 / sqrt(1 + r2)),
|
||
|
v = (1 - r2) + r * (1 + r2) * z,
|
||
|
p2 = (1 - sinPhi) / v,
|
||
|
p = sqrt(p2),
|
||
|
a2 = p2 * (1 + r2),
|
||
|
h = p * (1 - r2),
|
||
|
g2 = a2 - x * x,
|
||
|
g = sqrt(g2),
|
||
|
y0 = y + h + r * g;
|
||
|
if (abs(x1 - x0) < epsilon2 || --i === 0 || y0 === 0) break;
|
||
|
if (y0 > 0) x0 = r;
|
||
|
else x1 = r;
|
||
|
r = 0.5 * (x0 + x1);
|
||
|
}
|
||
|
|
||
|
if (!i) return null;
|
||
|
|
||
|
var phi = asin(sinPhi),
|
||
|
cosPhi = cos(phi),
|
||
|
secPhi = 1 / cosPhi,
|
||
|
drdPhi = 2 * sinPhi * cosPhi,
|
||
|
dvdPhi = (-3 * r + z * (1 + 3 * r2)) * drdPhi,
|
||
|
dp2dPhi = (-v * cosPhi - (1 - sinPhi) * dvdPhi) / (v * v),
|
||
|
dpdPhi = 0.5 * dp2dPhi / p,
|
||
|
dhdPhi = (1 - r2) * dpdPhi - 2 * r * p * drdPhi,
|
||
|
zeta = -2 * secPhi * dhdPhi,
|
||
|
mu = -secPhi * drdPhi,
|
||
|
nu = -secPhi * (r * (1 + r2) * dp2dPhi + p2 * (1 + 3 * r2) * drdPhi);
|
||
|
|
||
|
return [pi / 4 * (x * (zeta + mu * g) + nu * asin(x / sqrt(a2))), phi];
|
||
|
}
|
||
|
|
||
|
function gringorten() {
|
||
|
return d3Geo.geoProjection(squareRaw(gringortenRaw))
|
||
|
.scale(239.75);
|
||
|
}
|
||
|
|
||
|
// Returns [sn, cn, dn](u + iv|m).
|
||
|
function ellipticJi(u, v, m) {
|
||
|
var a, b, c;
|
||
|
if (!u) {
|
||
|
b = ellipticJ(v, 1 - m);
|
||
|
return [
|
||
|
[0, b[0] / b[1]],
|
||
|
[1 / b[1], 0],
|
||
|
[b[2] / b[1], 0]
|
||
|
];
|
||
|
}
|
||
|
a = ellipticJ(u, m);
|
||
|
if (!v) return [[a[0], 0], [a[1], 0], [a[2], 0]];
|
||
|
b = ellipticJ(v, 1 - m);
|
||
|
c = b[1] * b[1] + m * a[0] * a[0] * b[0] * b[0];
|
||
|
return [
|
||
|
[a[0] * b[2] / c, a[1] * a[2] * b[0] * b[1] / c],
|
||
|
[a[1] * b[1] / c, -a[0] * a[2] * b[0] * b[2] / c],
|
||
|
[a[2] * b[1] * b[2] / c, -m * a[0] * a[1] * b[0] / c]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Returns [sn, cn, dn, ph](u|m).
|
||
|
function ellipticJ(u, m) {
|
||
|
var ai, b, phi, t, twon;
|
||
|
if (m < epsilon) {
|
||
|
t = sin(u);
|
||
|
b = cos(u);
|
||
|
ai = m * (u - t * b) / 4;
|
||
|
return [
|
||
|
t - ai * b,
|
||
|
b + ai * t,
|
||
|
1 - m * t * t / 2,
|
||
|
u - ai
|
||
|
];
|
||
|
}
|
||
|
if (m >= 1 - epsilon) {
|
||
|
ai = (1 - m) / 4;
|
||
|
b = cosh(u);
|
||
|
t = tanh(u);
|
||
|
phi = 1 / b;
|
||
|
twon = b * sinh(u);
|
||
|
return [
|
||
|
t + ai * (twon - u) / (b * b),
|
||
|
phi - ai * t * phi * (twon - u),
|
||
|
phi + ai * t * phi * (twon + u),
|
||
|
2 * atan(exp(u)) - halfPi + ai * (twon - u) / b
|
||
|
];
|
||
|
}
|
||
|
|
||
|
var a = [1, 0, 0, 0, 0, 0, 0, 0, 0],
|
||
|
c = [sqrt(m), 0, 0, 0, 0, 0, 0, 0, 0],
|
||
|
i = 0;
|
||
|
b = sqrt(1 - m);
|
||
|
twon = 1;
|
||
|
|
||
|
while (abs(c[i] / a[i]) > epsilon && i < 8) {
|
||
|
ai = a[i++];
|
||
|
c[i] = (ai - b) / 2;
|
||
|
a[i] = (ai + b) / 2;
|
||
|
b = sqrt(ai * b);
|
||
|
twon *= 2;
|
||
|
}
|
||
|
|
||
|
phi = twon * a[i] * u;
|
||
|
do {
|
||
|
t = c[i] * sin(b = phi) / a[i];
|
||
|
phi = (asin(t) + phi) / 2;
|
||
|
} while (--i);
|
||
|
|
||
|
return [sin(phi), t = cos(phi), t / cos(phi - b), phi];
|
||
|
}
|
||
|
|
||
|
// Calculate F(phi+iPsi|m).
|
||
|
// See Abramowitz and Stegun, 17.4.11.
|
||
|
function ellipticFi(phi, psi, m) {
|
||
|
var r = abs(phi),
|
||
|
i = abs(psi),
|
||
|
sinhPsi = sinh(i);
|
||
|
if (r) {
|
||
|
var cscPhi = 1 / sin(r),
|
||
|
cotPhi2 = 1 / (tan(r) * tan(r)),
|
||
|
b = -(cotPhi2 + m * (sinhPsi * sinhPsi * cscPhi * cscPhi) - 1 + m),
|
||
|
c = (m - 1) * cotPhi2,
|
||
|
cotLambda2 = (-b + sqrt(b * b - 4 * c)) / 2;
|
||
|
return [
|
||
|
ellipticF(atan(1 / sqrt(cotLambda2)), m) * sign(phi),
|
||
|
ellipticF(atan(sqrt((cotLambda2 / cotPhi2 - 1) / m)), 1 - m) * sign(psi)
|
||
|
];
|
||
|
}
|
||
|
return [
|
||
|
0,
|
||
|
ellipticF(atan(sinhPsi), 1 - m) * sign(psi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Calculate F(phi|m) where m = k² = sin²α.
|
||
|
// See Abramowitz and Stegun, 17.6.7.
|
||
|
function ellipticF(phi, m) {
|
||
|
if (!m) return phi;
|
||
|
if (m === 1) return log(tan(phi / 2 + quarterPi));
|
||
|
var a = 1,
|
||
|
b = sqrt(1 - m),
|
||
|
c = sqrt(m);
|
||
|
for (var i = 0; abs(c) > epsilon; i++) {
|
||
|
if (phi % pi) {
|
||
|
var dPhi = atan(b * tan(phi) / a);
|
||
|
if (dPhi < 0) dPhi += pi;
|
||
|
phi += dPhi + ~~(phi / pi) * pi;
|
||
|
} else phi += phi;
|
||
|
c = (a + b) / 2;
|
||
|
b = sqrt(a * b);
|
||
|
c = ((a = c) - b) / 2;
|
||
|
}
|
||
|
return phi / (pow(2, i) * a);
|
||
|
}
|
||
|
|
||
|
function guyouRaw(lambda, phi) {
|
||
|
var k_ = (sqrt2 - 1) / (sqrt2 + 1),
|
||
|
k = sqrt(1 - k_ * k_),
|
||
|
K = ellipticF(halfPi, k * k),
|
||
|
f = -1,
|
||
|
psi = log(tan(pi / 4 + abs(phi) / 2)),
|
||
|
r = exp(f * psi) / sqrt(k_),
|
||
|
at = guyouComplexAtan(r * cos(f * lambda), r * sin(f * lambda)),
|
||
|
t = ellipticFi(at[0], at[1], k * k);
|
||
|
return [-t[1], (phi >= 0 ? 1 : -1) * (0.5 * K - t[0])];
|
||
|
}
|
||
|
|
||
|
function guyouComplexAtan(x, y) {
|
||
|
var x2 = x * x,
|
||
|
y_1 = y + 1,
|
||
|
t = 1 - x2 - y * y;
|
||
|
return [
|
||
|
0.5 * ((x >= 0 ? halfPi : -halfPi) - atan2(t, 2 * x)),
|
||
|
-0.25 * log(t * t + 4 * x2) +0.5 * log(y_1 * y_1 + x2)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function guyouComplexDivide(a, b) {
|
||
|
var denominator = b[0] * b[0] + b[1] * b[1];
|
||
|
return [
|
||
|
(a[0] * b[0] + a[1] * b[1]) / denominator,
|
||
|
(a[1] * b[0] - a[0] * b[1]) / denominator
|
||
|
];
|
||
|
}
|
||
|
|
||
|
guyouRaw.invert = function(x, y) {
|
||
|
var k_ = (sqrt2 - 1) / (sqrt2 + 1),
|
||
|
k = sqrt(1 - k_ * k_),
|
||
|
K = ellipticF(halfPi, k * k),
|
||
|
f = -1,
|
||
|
j = ellipticJi(0.5 * K - y, -x, k * k),
|
||
|
tn = guyouComplexDivide(j[0], j[1]),
|
||
|
lambda = atan2(tn[1], tn[0]) / f;
|
||
|
return [
|
||
|
lambda,
|
||
|
2 * atan(exp(0.5 / f * log(k_ * tn[0] * tn[0] + k_ * tn[1] * tn[1]))) - halfPi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function guyou() {
|
||
|
return d3Geo.geoProjection(squareRaw(guyouRaw))
|
||
|
.scale(151.496);
|
||
|
}
|
||
|
|
||
|
function hammerRetroazimuthalRaw(phi0) {
|
||
|
var sinPhi0 = sin(phi0),
|
||
|
cosPhi0 = cos(phi0),
|
||
|
rotate = hammerRetroazimuthalRotation(phi0);
|
||
|
|
||
|
rotate.invert = hammerRetroazimuthalRotation(-phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var p = rotate(lambda, phi);
|
||
|
lambda = p[0], phi = p[1];
|
||
|
var sinPhi = sin(phi),
|
||
|
cosPhi = cos(phi),
|
||
|
cosLambda = cos(lambda),
|
||
|
z = acos(sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosLambda),
|
||
|
sinz = sin(z),
|
||
|
K = abs(sinz) > epsilon ? z / sinz : 1;
|
||
|
return [
|
||
|
K * cosPhi0 * sin(lambda),
|
||
|
(abs(lambda) > halfPi ? K : -K) // rotate for back hemisphere
|
||
|
* (sinPhi0 * cosPhi - cosPhi0 * sinPhi * cosLambda)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var rho = sqrt(x * x + y * y),
|
||
|
sinz = -sin(rho),
|
||
|
cosz = cos(rho),
|
||
|
a = rho * cosz,
|
||
|
b = -y * sinz,
|
||
|
c = rho * sinPhi0,
|
||
|
d = sqrt(a * a + b * b - c * c),
|
||
|
phi = atan2(a * c + b * d, b * c - a * d),
|
||
|
lambda = (rho > halfPi ? -1 : 1) * atan2(x * sinz, rho * cos(phi) * cosz + y * sin(phi) * sinz);
|
||
|
return rotate.invert(lambda, phi);
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
// Latitudinal rotation by phi0.
|
||
|
// Temporary hack until D3 supports arbitrary small-circle clipping origins.
|
||
|
function hammerRetroazimuthalRotation(phi0) {
|
||
|
var sinPhi0 = sin(phi0),
|
||
|
cosPhi0 = cos(phi0);
|
||
|
|
||
|
return function(lambda, phi) {
|
||
|
var cosPhi = cos(phi),
|
||
|
x = cos(lambda) * cosPhi,
|
||
|
y = sin(lambda) * cosPhi,
|
||
|
z = sin(phi);
|
||
|
return [
|
||
|
atan2(y, x * cosPhi0 - z * sinPhi0),
|
||
|
asin(z * cosPhi0 + x * sinPhi0)
|
||
|
];
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function hammerRetroazimuthal() {
|
||
|
var phi0 = 0,
|
||
|
m = d3Geo.geoProjectionMutator(hammerRetroazimuthalRaw),
|
||
|
p = m(phi0),
|
||
|
rotate_ = p.rotate,
|
||
|
stream_ = p.stream,
|
||
|
circle = d3Geo.geoCircle();
|
||
|
|
||
|
p.parallel = function(_) {
|
||
|
if (!arguments.length) return phi0 * degrees;
|
||
|
var r = p.rotate();
|
||
|
return m(phi0 = _ * radians).rotate(r);
|
||
|
};
|
||
|
|
||
|
// Temporary hack; see hammerRetroazimuthalRotation.
|
||
|
p.rotate = function(_) {
|
||
|
if (!arguments.length) return (_ = rotate_.call(p), _[1] += phi0 * degrees, _);
|
||
|
rotate_.call(p, [_[0], _[1] - phi0 * degrees]);
|
||
|
circle.center([-_[0], -_[1]]);
|
||
|
return p;
|
||
|
};
|
||
|
|
||
|
p.stream = function(stream) {
|
||
|
stream = stream_(stream);
|
||
|
stream.sphere = function() {
|
||
|
stream.polygonStart();
|
||
|
var epsilon$$1 = 1e-2,
|
||
|
ring = circle.radius(90 - epsilon$$1)().coordinates[0],
|
||
|
n = ring.length - 1,
|
||
|
i = -1,
|
||
|
p;
|
||
|
stream.lineStart();
|
||
|
while (++i < n) stream.point((p = ring[i])[0], p[1]);
|
||
|
stream.lineEnd();
|
||
|
ring = circle.radius(90 + epsilon$$1)().coordinates[0];
|
||
|
n = ring.length - 1;
|
||
|
stream.lineStart();
|
||
|
while (--i >= 0) stream.point((p = ring[i])[0], p[1]);
|
||
|
stream.lineEnd();
|
||
|
stream.polygonEnd();
|
||
|
};
|
||
|
return stream;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(79.4187)
|
||
|
.parallel(45)
|
||
|
.clipAngle(180 - 1e-3);
|
||
|
}
|
||
|
|
||
|
var K = 3,
|
||
|
healpixParallel = asin(1 - 1 / K) * degrees,
|
||
|
healpixLambert = cylindricalEqualAreaRaw(0);
|
||
|
|
||
|
function healpixRaw(H) {
|
||
|
var phi0 = healpixParallel * radians,
|
||
|
dx = collignonRaw(pi, phi0)[0] - collignonRaw(-pi, phi0)[0],
|
||
|
y0 = healpixLambert(0, phi0)[1],
|
||
|
y1 = collignonRaw(0, phi0)[1],
|
||
|
dy1 = sqrtPi - y1,
|
||
|
k = tau / H,
|
||
|
w = 4 / tau,
|
||
|
h = y0 + (dy1 * dy1 * 4) / tau;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var point,
|
||
|
phi2 = abs(phi);
|
||
|
if (phi2 > phi0) {
|
||
|
var i = min(H - 1, max(0, floor((lambda + pi) / k)));
|
||
|
lambda += pi * (H - 1) / H - i * k;
|
||
|
point = collignonRaw(lambda, phi2);
|
||
|
point[0] = point[0] * tau / dx - tau * (H - 1) / (2 * H) + i * tau / H;
|
||
|
point[1] = y0 + (point[1] - y1) * 4 * dy1 / tau;
|
||
|
if (phi < 0) point[1] = -point[1];
|
||
|
} else {
|
||
|
point = healpixLambert(lambda, phi);
|
||
|
}
|
||
|
point[0] *= w, point[1] /= h;
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
x /= w, y *= h;
|
||
|
var y2 = abs(y);
|
||
|
if (y2 > y0) {
|
||
|
var i = min(H - 1, max(0, floor((x + pi) / k)));
|
||
|
x = (x + pi * (H - 1) / H - i * k) * dx / tau;
|
||
|
var point = collignonRaw.invert(x, 0.25 * (y2 - y0) * tau / dy1 + y1);
|
||
|
point[0] -= pi * (H - 1) / H - i * k;
|
||
|
if (y < 0) point[1] = -point[1];
|
||
|
return point;
|
||
|
}
|
||
|
return healpixLambert.invert(x, y);
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function sphereTop(x, i) {
|
||
|
return [x, i & 1 ? 90 - epsilon : healpixParallel];
|
||
|
}
|
||
|
|
||
|
function sphereBottom(x, i) {
|
||
|
return [x, i & 1 ? -90 + epsilon : -healpixParallel];
|
||
|
}
|
||
|
|
||
|
function sphereNudge(d) {
|
||
|
return [d[0] * (1 - epsilon), d[1]];
|
||
|
}
|
||
|
|
||
|
function sphere(step) {
|
||
|
var c = [].concat(
|
||
|
d3Array.range(-180, 180 + step / 2, step).map(sphereTop),
|
||
|
d3Array.range(180, -180 - step / 2, -step).map(sphereBottom)
|
||
|
);
|
||
|
return {
|
||
|
type: "Polygon",
|
||
|
coordinates: [step === 180 ? c.map(sphereNudge) : c]
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function healpix() {
|
||
|
var H = 4,
|
||
|
m = d3Geo.geoProjectionMutator(healpixRaw),
|
||
|
p = m(H),
|
||
|
stream_ = p.stream;
|
||
|
|
||
|
p.lobes = function(_) {
|
||
|
return arguments.length ? m(H = +_) : H;
|
||
|
};
|
||
|
|
||
|
p.stream = function(stream) {
|
||
|
var rotate = p.rotate(),
|
||
|
rotateStream = stream_(stream),
|
||
|
sphereStream = (p.rotate([0, 0]), stream_(stream));
|
||
|
p.rotate(rotate);
|
||
|
rotateStream.sphere = function() { d3Geo.geoStream(sphere(180 / H), sphereStream); };
|
||
|
return rotateStream;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(239.75);
|
||
|
}
|
||
|
|
||
|
function hillRaw(K) {
|
||
|
var L = 1 + K,
|
||
|
sinBt = sin(1 / L),
|
||
|
Bt = asin(sinBt),
|
||
|
A = 2 * sqrt(pi / (B = pi + 4 * Bt * L)),
|
||
|
B,
|
||
|
rho0 = 0.5 * A * (L + sqrt(K * (2 + K))),
|
||
|
K2 = K * K,
|
||
|
L2 = L * L;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var t = 1 - sin(phi),
|
||
|
rho,
|
||
|
omega;
|
||
|
if (t && t < 2) {
|
||
|
var theta = halfPi - phi, i = 25, delta;
|
||
|
do {
|
||
|
var sinTheta = sin(theta),
|
||
|
cosTheta = cos(theta),
|
||
|
Bt_Bt1 = Bt + atan2(sinTheta, L - cosTheta),
|
||
|
C = 1 + L2 - 2 * L * cosTheta;
|
||
|
theta -= delta = (theta - K2 * Bt - L * sinTheta + C * Bt_Bt1 -0.5 * t * B) / (2 * L * sinTheta * Bt_Bt1);
|
||
|
} while (abs(delta) > epsilon2 && --i > 0);
|
||
|
rho = A * sqrt(C);
|
||
|
omega = lambda * Bt_Bt1 / pi;
|
||
|
} else {
|
||
|
rho = A * (K + t);
|
||
|
omega = lambda * Bt / pi;
|
||
|
}
|
||
|
return [
|
||
|
rho * sin(omega),
|
||
|
rho0 - rho * cos(omega)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var rho2 = x * x + (y -= rho0) * y,
|
||
|
cosTheta = (1 + L2 - rho2 / (A * A)) / (2 * L),
|
||
|
theta = acos(cosTheta),
|
||
|
sinTheta = sin(theta),
|
||
|
Bt_Bt1 = Bt + atan2(sinTheta, L - cosTheta);
|
||
|
return [
|
||
|
asin(x / sqrt(rho2)) * pi / Bt_Bt1,
|
||
|
asin(1 - 2 * (theta - K2 * Bt - L * sinTheta + (1 + L2 - 2 * L * cosTheta) * Bt_Bt1) / B)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function hill() {
|
||
|
var K = 1,
|
||
|
m = d3Geo.geoProjectionMutator(hillRaw),
|
||
|
p = m(K);
|
||
|
|
||
|
p.ratio = function(_) {
|
||
|
return arguments.length ? m(K = +_) : K;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(167.774)
|
||
|
.center([0, 18.67]);
|
||
|
}
|
||
|
|
||
|
var sinuMollweidePhi = 0.7109889596207567;
|
||
|
|
||
|
var sinuMollweideY = 0.0528035274542;
|
||
|
|
||
|
function sinuMollweideRaw(lambda, phi) {
|
||
|
return phi > -sinuMollweidePhi
|
||
|
? (lambda = mollweideRaw(lambda, phi), lambda[1] += sinuMollweideY, lambda)
|
||
|
: sinusoidalRaw(lambda, phi);
|
||
|
}
|
||
|
|
||
|
sinuMollweideRaw.invert = function(x, y) {
|
||
|
return y > -sinuMollweidePhi
|
||
|
? mollweideRaw.invert(x, y - sinuMollweideY)
|
||
|
: sinusoidalRaw.invert(x, y);
|
||
|
};
|
||
|
|
||
|
function sinuMollweide() {
|
||
|
return d3Geo.geoProjection(sinuMollweideRaw)
|
||
|
.rotate([-20, -55])
|
||
|
.scale(164.263)
|
||
|
.center([0, -5.4036]);
|
||
|
}
|
||
|
|
||
|
function homolosineRaw(lambda, phi) {
|
||
|
return abs(phi) > sinuMollweidePhi
|
||
|
? (lambda = mollweideRaw(lambda, phi), lambda[1] -= phi > 0 ? sinuMollweideY : -sinuMollweideY, lambda)
|
||
|
: sinusoidalRaw(lambda, phi);
|
||
|
}
|
||
|
|
||
|
homolosineRaw.invert = function(x, y) {
|
||
|
return abs(y) > sinuMollweidePhi
|
||
|
? mollweideRaw.invert(x, y + (y > 0 ? sinuMollweideY : -sinuMollweideY))
|
||
|
: sinusoidalRaw.invert(x, y);
|
||
|
};
|
||
|
|
||
|
function homolosine() {
|
||
|
return d3Geo.geoProjection(homolosineRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function hufnagelRaw(a, b, psiMax, ratio) {
|
||
|
var k = sqrt(
|
||
|
(4 * pi) /
|
||
|
(2 * psiMax +
|
||
|
(1 + a - b / 2) * sin(2 * psiMax) +
|
||
|
((a + b) / 2) * sin(4 * psiMax) +
|
||
|
(b / 2) * sin(6 * psiMax))
|
||
|
),
|
||
|
c = sqrt(
|
||
|
ratio *
|
||
|
sin(psiMax) *
|
||
|
sqrt((1 + a * cos(2 * psiMax) + b * cos(4 * psiMax)) / (1 + a + b))
|
||
|
),
|
||
|
M = psiMax * mapping(1);
|
||
|
|
||
|
function radius(psi) {
|
||
|
return sqrt(1 + a * cos(2 * psi) + b * cos(4 * psi));
|
||
|
}
|
||
|
|
||
|
function mapping(t) {
|
||
|
var psi = t * psiMax;
|
||
|
return (
|
||
|
(2 * psi +
|
||
|
(1 + a - b / 2) * sin(2 * psi) +
|
||
|
((a + b) / 2) * sin(4 * psi) +
|
||
|
(b / 2) * sin(6 * psi)) /
|
||
|
psiMax
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function inversemapping(psi) {
|
||
|
return radius(psi) * sin(psi);
|
||
|
}
|
||
|
|
||
|
var forward = function(lambda, phi) {
|
||
|
var psi = psiMax * solve(mapping, (M * sin(phi)) / psiMax, phi / pi);
|
||
|
if (isNaN(psi)) psi = psiMax * sign(phi);
|
||
|
var kr = k * radius(psi);
|
||
|
return [((kr * c * lambda) / pi) * cos(psi), (kr / c) * sin(psi)];
|
||
|
};
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var psi = solve(inversemapping, (y * c) / k);
|
||
|
return [
|
||
|
(x * pi) / (cos(psi) * k * c * radius(psi)),
|
||
|
asin((psiMax * mapping(psi / psiMax)) / M)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
if (psiMax === 0) {
|
||
|
k = sqrt(ratio / pi);
|
||
|
forward = function(lambda, phi) {
|
||
|
return [lambda * k, sin(phi) / k];
|
||
|
};
|
||
|
forward.invert = function(x, y) {
|
||
|
return [x / k, asin(y * k)];
|
||
|
};
|
||
|
}
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function hufnagel() {
|
||
|
var a = 1,
|
||
|
b = 0,
|
||
|
psiMax = 45 * radians,
|
||
|
ratio = 2,
|
||
|
mutate = d3Geo.geoProjectionMutator(hufnagelRaw),
|
||
|
projection = mutate(a, b, psiMax, ratio);
|
||
|
|
||
|
projection.a = function(_) {
|
||
|
return arguments.length ? mutate((a = +_), b, psiMax, ratio) : a;
|
||
|
};
|
||
|
projection.b = function(_) {
|
||
|
return arguments.length ? mutate(a, (b = +_), psiMax, ratio) : b;
|
||
|
};
|
||
|
projection.psiMax = function(_) {
|
||
|
return arguments.length
|
||
|
? mutate(a, b, (psiMax = +_ * radians), ratio)
|
||
|
: psiMax * degrees;
|
||
|
};
|
||
|
projection.ratio = function(_) {
|
||
|
return arguments.length ? mutate(a, b, psiMax, (ratio = +_)) : ratio;
|
||
|
};
|
||
|
|
||
|
return projection.scale(180.739);
|
||
|
}
|
||
|
|
||
|
// https://github.com/scijs/integrate-adaptive-simpson
|
||
|
|
||
|
// This algorithm adapted from pseudocode in:
|
||
|
// http://www.math.utk.edu/~ccollins/refs/Handouts/rich.pdf
|
||
|
function adsimp (f, a, b, fa, fm, fb, V0, tol, maxdepth, depth, state) {
|
||
|
if (state.nanEncountered) {
|
||
|
return NaN;
|
||
|
}
|
||
|
|
||
|
var h, f1, f2, sl, sr, s2, m, V1, V2, err;
|
||
|
|
||
|
h = b - a;
|
||
|
f1 = f(a + h * 0.25);
|
||
|
f2 = f(b - h * 0.25);
|
||
|
|
||
|
// Simple check for NaN:
|
||
|
if (isNaN(f1)) {
|
||
|
state.nanEncountered = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Simple check for NaN:
|
||
|
if (isNaN(f2)) {
|
||
|
state.nanEncountered = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sl = h * (fa + 4 * f1 + fm) / 12;
|
||
|
sr = h * (fm + 4 * f2 + fb) / 12;
|
||
|
s2 = sl + sr;
|
||
|
err = (s2 - V0) / 15;
|
||
|
|
||
|
if (depth > maxdepth) {
|
||
|
state.maxDepthCount++;
|
||
|
return s2 + err;
|
||
|
} else if (Math.abs(err) < tol) {
|
||
|
return s2 + err;
|
||
|
} else {
|
||
|
m = a + h * 0.5;
|
||
|
|
||
|
V1 = adsimp(f, a, m, fa, f1, fm, sl, tol * 0.5, maxdepth, depth + 1, state);
|
||
|
|
||
|
if (isNaN(V1)) {
|
||
|
state.nanEncountered = true;
|
||
|
return NaN;
|
||
|
}
|
||
|
|
||
|
V2 = adsimp(f, m, b, fm, f2, fb, sr, tol * 0.5, maxdepth, depth + 1, state);
|
||
|
|
||
|
if (isNaN(V2)) {
|
||
|
state.nanEncountered = true;
|
||
|
return NaN;
|
||
|
}
|
||
|
|
||
|
return V1 + V2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function integrate (f, a, b, tol, maxdepth) {
|
||
|
var state = {
|
||
|
maxDepthCount: 0,
|
||
|
nanEncountered: false
|
||
|
};
|
||
|
|
||
|
if (tol === undefined) {
|
||
|
tol = 1e-8;
|
||
|
}
|
||
|
if (maxdepth === undefined) {
|
||
|
maxdepth = 20;
|
||
|
}
|
||
|
|
||
|
var fa = f(a);
|
||
|
var fm = f(0.5 * (a + b));
|
||
|
var fb = f(b);
|
||
|
|
||
|
var V0 = (fa + 4 * fm + fb) * (b - a) / 6;
|
||
|
|
||
|
var result = adsimp(f, a, b, fa, fm, fb, V0, tol, maxdepth, 1, state);
|
||
|
|
||
|
/*
|
||
|
if (state.maxDepthCount > 0 && console && console.warn) {
|
||
|
console.warn('integrate-adaptive-simpson: Warning: maximum recursion depth (' + maxdepth + ') reached ' + state.maxDepthCount + ' times');
|
||
|
}
|
||
|
|
||
|
if (state.nanEncountered && console && console.warn) {
|
||
|
console.warn('integrate-adaptive-simpson: Warning: NaN encountered. Halting early.');
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function hyperellipticalRaw(alpha, k, gamma) {
|
||
|
|
||
|
function elliptic (f) {
|
||
|
return alpha + (1 - alpha) * pow(1 - pow(f, k), 1 / k);
|
||
|
}
|
||
|
|
||
|
function z(f) {
|
||
|
return integrate(elliptic, 0, f, 1e-4);
|
||
|
}
|
||
|
|
||
|
var G = 1 / z(1),
|
||
|
n = 1000,
|
||
|
m = (1 + 1e-8) * G,
|
||
|
approx = [];
|
||
|
for (var i = 0; i <= n; i++)
|
||
|
approx.push(z(i / n) * m);
|
||
|
|
||
|
function Y(sinphi) {
|
||
|
var rmin = 0, rmax = n, r = n >> 1;
|
||
|
do {
|
||
|
if (approx[r] > sinphi) rmax = r; else rmin = r;
|
||
|
r = (rmin + rmax) >> 1;
|
||
|
} while (r > rmin);
|
||
|
var u = approx[r + 1] - approx[r];
|
||
|
if (u) u = (sinphi - approx[r + 1]) / u;
|
||
|
return (r + 1 + u) / n;
|
||
|
}
|
||
|
|
||
|
var ratio = 2 * Y(1) / pi * G / gamma;
|
||
|
|
||
|
var forward = function(lambda, phi) {
|
||
|
var y = Y(abs(sin(phi))),
|
||
|
x = elliptic(y) * lambda;
|
||
|
y /= ratio;
|
||
|
return [ x, (phi >= 0) ? y : -y ];
|
||
|
};
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var phi;
|
||
|
y *= ratio;
|
||
|
if (abs(y) < 1) phi = sign(y) * asin(z(abs(y)) * G);
|
||
|
return [ x / elliptic(abs(y)), phi ];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function hyperelliptical() {
|
||
|
var alpha = 0,
|
||
|
k = 2.5,
|
||
|
gamma = 1.183136, // affine = sqrt(2 * gamma / pi) = 0.8679
|
||
|
m = d3Geo.geoProjectionMutator(hyperellipticalRaw),
|
||
|
p = m(alpha, k, gamma);
|
||
|
|
||
|
p.alpha = function(_) {
|
||
|
return arguments.length ? m(alpha = +_, k, gamma) : alpha;
|
||
|
};
|
||
|
|
||
|
p.k = function(_) {
|
||
|
return arguments.length ? m(alpha, k = +_, gamma) : k;
|
||
|
};
|
||
|
|
||
|
p.gamma = function(_) {
|
||
|
return arguments.length ? m(alpha, k, gamma = +_) : gamma;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function pointEqual(a, b) {
|
||
|
return abs(a[0] - b[0]) < epsilon && abs(a[1] - b[1]) < epsilon;
|
||
|
}
|
||
|
|
||
|
function interpolateLine(coordinates, m) {
|
||
|
var i = -1,
|
||
|
n = coordinates.length,
|
||
|
p0 = coordinates[0],
|
||
|
p1,
|
||
|
dx,
|
||
|
dy,
|
||
|
resampled = [];
|
||
|
while (++i < n) {
|
||
|
p1 = coordinates[i];
|
||
|
dx = (p1[0] - p0[0]) / m;
|
||
|
dy = (p1[1] - p0[1]) / m;
|
||
|
for (var j = 0; j < m; ++j) resampled.push([p0[0] + j * dx, p0[1] + j * dy]);
|
||
|
p0 = p1;
|
||
|
}
|
||
|
resampled.push(p1);
|
||
|
return resampled;
|
||
|
}
|
||
|
|
||
|
function interpolateSphere(lobes) {
|
||
|
var coordinates = [],
|
||
|
lobe,
|
||
|
lambda0, phi0, phi1,
|
||
|
lambda2, phi2,
|
||
|
i, n = lobes[0].length;
|
||
|
|
||
|
// Northern Hemisphere
|
||
|
for (i = 0; i < n; ++i) {
|
||
|
lobe = lobes[0][i];
|
||
|
lambda0 = lobe[0][0], phi0 = lobe[0][1], phi1 = lobe[1][1];
|
||
|
lambda2 = lobe[2][0], phi2 = lobe[2][1];
|
||
|
coordinates.push(interpolateLine([
|
||
|
[lambda0 + epsilon, phi0 + epsilon],
|
||
|
[lambda0 + epsilon, phi1 - epsilon],
|
||
|
[lambda2 - epsilon, phi1 - epsilon],
|
||
|
[lambda2 - epsilon, phi2 + epsilon]
|
||
|
], 30));
|
||
|
}
|
||
|
|
||
|
// Southern Hemisphere
|
||
|
for (i = lobes[1].length - 1; i >= 0; --i) {
|
||
|
lobe = lobes[1][i];
|
||
|
lambda0 = lobe[0][0], phi0 = lobe[0][1], phi1 = lobe[1][1];
|
||
|
lambda2 = lobe[2][0], phi2 = lobe[2][1];
|
||
|
coordinates.push(interpolateLine([
|
||
|
[lambda2 - epsilon, phi2 - epsilon],
|
||
|
[lambda2 - epsilon, phi1 + epsilon],
|
||
|
[lambda0 + epsilon, phi1 + epsilon],
|
||
|
[lambda0 + epsilon, phi0 - epsilon]
|
||
|
], 30));
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
type: "Polygon",
|
||
|
coordinates: [d3Array.merge(coordinates)]
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function interrupt(project, lobes, inverse) {
|
||
|
var sphere, bounds;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var sign$$1 = phi < 0 ? -1 : +1, lobe = lobes[+(phi < 0)];
|
||
|
for (var i = 0, n = lobe.length - 1; i < n && lambda > lobe[i][2][0]; ++i);
|
||
|
var p = project(lambda - lobe[i][1][0], phi);
|
||
|
p[0] += project(lobe[i][1][0], sign$$1 * phi > sign$$1 * lobe[i][0][1] ? lobe[i][0][1] : phi)[0];
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
if (inverse) {
|
||
|
forward.invert = inverse(forward);
|
||
|
} else if (project.invert) {
|
||
|
forward.invert = function(x, y) {
|
||
|
var bound = bounds[+(y < 0)], lobe = lobes[+(y < 0)];
|
||
|
for (var i = 0, n = bound.length; i < n; ++i) {
|
||
|
var b = bound[i];
|
||
|
if (b[0][0] <= x && x < b[1][0] && b[0][1] <= y && y < b[1][1]) {
|
||
|
var p = project.invert(x - project(lobe[i][1][0], 0)[0], y);
|
||
|
p[0] += lobe[i][1][0];
|
||
|
return pointEqual(forward(p[0], p[1]), [x, y]) ? p : null;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var p = d3Geo.geoProjection(forward),
|
||
|
stream_ = p.stream;
|
||
|
|
||
|
p.stream = function(stream) {
|
||
|
var rotate = p.rotate(),
|
||
|
rotateStream = stream_(stream),
|
||
|
sphereStream = (p.rotate([0, 0]), stream_(stream));
|
||
|
p.rotate(rotate);
|
||
|
rotateStream.sphere = function() { d3Geo.geoStream(sphere, sphereStream); };
|
||
|
return rotateStream;
|
||
|
};
|
||
|
|
||
|
p.lobes = function(_) {
|
||
|
if (!arguments.length) return lobes.map(function(lobe) {
|
||
|
return lobe.map(function(l) {
|
||
|
return [
|
||
|
[l[0][0] * degrees, l[0][1] * degrees],
|
||
|
[l[1][0] * degrees, l[1][1] * degrees],
|
||
|
[l[2][0] * degrees, l[2][1] * degrees]
|
||
|
];
|
||
|
});
|
||
|
});
|
||
|
|
||
|
sphere = interpolateSphere(_);
|
||
|
|
||
|
lobes = _.map(function(lobe) {
|
||
|
return lobe.map(function(l) {
|
||
|
return [
|
||
|
[l[0][0] * radians, l[0][1] * radians],
|
||
|
[l[1][0] * radians, l[1][1] * radians],
|
||
|
[l[2][0] * radians, l[2][1] * radians]
|
||
|
];
|
||
|
});
|
||
|
});
|
||
|
|
||
|
bounds = lobes.map(function(lobe) {
|
||
|
return lobe.map(function(l) {
|
||
|
var x0 = project(l[0][0], l[0][1])[0],
|
||
|
x1 = project(l[2][0], l[2][1])[0],
|
||
|
y0 = project(l[1][0], l[0][1])[1],
|
||
|
y1 = project(l[1][0], l[1][1])[1],
|
||
|
t;
|
||
|
if (y0 > y1) t = y0, y0 = y1, y1 = t;
|
||
|
return [[x0, y0], [x1, y1]];
|
||
|
});
|
||
|
});
|
||
|
|
||
|
return p;
|
||
|
};
|
||
|
|
||
|
if (lobes != null) p.lobes(lobes);
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
var lobes = [[ // northern hemisphere
|
||
|
[[-180, 0], [-100, 90], [ -40, 0]],
|
||
|
[[ -40, 0], [ 30, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [-160, -90], [-100, 0]],
|
||
|
[[-100, 0], [ -60, -90], [ -20, 0]],
|
||
|
[[ -20, 0], [ 20, -90], [ 80, 0]],
|
||
|
[[ 80, 0], [ 140, -90], [ 180, 0]]
|
||
|
]];
|
||
|
|
||
|
function boggs$1() {
|
||
|
return interrupt(boggsRaw, lobes)
|
||
|
.scale(160.857);
|
||
|
}
|
||
|
|
||
|
var lobes$1 = [[ // northern hemisphere
|
||
|
[[-180, 0], [-100, 90], [ -40, 0]],
|
||
|
[[ -40, 0], [ 30, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [-160, -90], [-100, 0]],
|
||
|
[[-100, 0], [ -60, -90], [ -20, 0]],
|
||
|
[[ -20, 0], [ 20, -90], [ 80, 0]],
|
||
|
[[ 80, 0], [ 140, -90], [ 180, 0]]
|
||
|
]];
|
||
|
|
||
|
function homolosine$1() {
|
||
|
return interrupt(homolosineRaw, lobes$1)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
var lobes$2 = [[ // northern hemisphere
|
||
|
[[-180, 0], [-100, 90], [ -40, 0]],
|
||
|
[[ -40, 0], [ 30, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [-160, -90], [-100, 0]],
|
||
|
[[-100, 0], [ -60, -90], [ -20, 0]],
|
||
|
[[ -20, 0], [ 20, -90], [ 80, 0]],
|
||
|
[[ 80, 0], [ 140, -90], [ 180, 0]]
|
||
|
]];
|
||
|
|
||
|
function mollweide$1() {
|
||
|
return interrupt(mollweideRaw, lobes$2)
|
||
|
.scale(169.529);
|
||
|
}
|
||
|
|
||
|
var lobes$3 = [[ // northern hemisphere
|
||
|
[[-180, 0], [ -90, 90], [ 0, 0]],
|
||
|
[[ 0, 0], [ 90, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [ -90, -90], [ 0, 0]],
|
||
|
[[ 0, 0], [ 90, -90], [ 180, 0]]
|
||
|
]];
|
||
|
|
||
|
function mollweideHemispheres() {
|
||
|
return interrupt(mollweideRaw, lobes$3)
|
||
|
.scale(169.529)
|
||
|
.rotate([20, 0]);
|
||
|
}
|
||
|
|
||
|
var lobes$4 = [[ // northern hemisphere
|
||
|
[[-180, 35], [ -30, 90], [ 0, 35]],
|
||
|
[[ 0, 35], [ 30, 90], [ 180, 35]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, -10], [-102, -90], [ -65, -10]],
|
||
|
[[ -65, -10], [ 5, -90], [ 77, -10]],
|
||
|
[[ 77, -10], [ 103, -90], [ 180, -10]]
|
||
|
]];
|
||
|
|
||
|
function sinuMollweide$1() {
|
||
|
return interrupt(sinuMollweideRaw, lobes$4, solve2d)
|
||
|
.rotate([-20, -55])
|
||
|
.scale(164.263)
|
||
|
.center([0, -5.4036]);
|
||
|
}
|
||
|
|
||
|
var lobes$5 = [[ // northern hemisphere
|
||
|
[[-180, 0], [-110, 90], [ -40, 0]],
|
||
|
[[ -40, 0], [ 0, 90], [ 40, 0]],
|
||
|
[[ 40, 0], [ 110, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [-110, -90], [ -40, 0]],
|
||
|
[[ -40, 0], [ 0, -90], [ 40, 0]],
|
||
|
[[ 40, 0], [ 110, -90], [ 180, 0]]
|
||
|
]];
|
||
|
|
||
|
function sinusoidal$1() {
|
||
|
return interrupt(sinusoidalRaw, lobes$5)
|
||
|
.scale(152.63)
|
||
|
.rotate([-20, 0]);
|
||
|
}
|
||
|
|
||
|
function kavrayskiy7Raw(lambda, phi) {
|
||
|
return [3 / tau * lambda * sqrt(pi * pi / 3 - phi * phi), phi];
|
||
|
}
|
||
|
|
||
|
kavrayskiy7Raw.invert = function(x, y) {
|
||
|
return [tau / 3 * x / sqrt(pi * pi / 3 - y * y), y];
|
||
|
};
|
||
|
|
||
|
function kavrayskiy7() {
|
||
|
return d3Geo.geoProjection(kavrayskiy7Raw)
|
||
|
.scale(158.837);
|
||
|
}
|
||
|
|
||
|
function lagrangeRaw(n) {
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
if (abs(abs(phi) - halfPi) < epsilon) return [0, phi < 0 ? -2 : 2];
|
||
|
var sinPhi = sin(phi),
|
||
|
v = pow((1 + sinPhi) / (1 - sinPhi), n / 2),
|
||
|
c = 0.5 * (v + 1 / v) + cos(lambda *= n);
|
||
|
return [
|
||
|
2 * sin(lambda) / c,
|
||
|
(v - 1 / v) / c
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var y0 = abs(y);
|
||
|
if (abs(y0 - 2) < epsilon) return x ? null : [0, sign(y) * halfPi];
|
||
|
if (y0 > 2) return null;
|
||
|
|
||
|
x /= 2, y /= 2;
|
||
|
var x2 = x * x,
|
||
|
y2 = y * y,
|
||
|
t = 2 * y / (1 + x2 + y2); // tanh(nPhi)
|
||
|
t = pow((1 + t) / (1 - t), 1 / n);
|
||
|
return [
|
||
|
atan2(2 * x, 1 - x2 - y2) / n,
|
||
|
asin((t - 1) / (t + 1))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function lagrange() {
|
||
|
var n = 0.5,
|
||
|
m = d3Geo.geoProjectionMutator(lagrangeRaw),
|
||
|
p = m(n);
|
||
|
|
||
|
p.spacing = function(_) {
|
||
|
return arguments.length ? m(n = +_) : n;
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(124.75);
|
||
|
}
|
||
|
|
||
|
var pi_sqrt2 = pi / sqrt2;
|
||
|
|
||
|
function larriveeRaw(lambda, phi) {
|
||
|
return [
|
||
|
lambda * (1 + sqrt(cos(phi))) / 2,
|
||
|
phi / (cos(phi / 2) * cos(lambda / 6))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
larriveeRaw.invert = function(x, y) {
|
||
|
var x0 = abs(x),
|
||
|
y0 = abs(y),
|
||
|
lambda = epsilon,
|
||
|
phi = halfPi;
|
||
|
if (y0 < pi_sqrt2) phi *= y0 / pi_sqrt2;
|
||
|
else lambda += 6 * acos(pi_sqrt2 / y0);
|
||
|
for (var i = 0; i < 25; i++) {
|
||
|
var sinPhi = sin(phi),
|
||
|
sqrtcosPhi = sqrt(cos(phi)),
|
||
|
sinPhi_2 = sin(phi / 2),
|
||
|
cosPhi_2 = cos(phi / 2),
|
||
|
sinLambda_6 = sin(lambda / 6),
|
||
|
cosLambda_6 = cos(lambda / 6),
|
||
|
f0 = 0.5 * lambda * (1 + sqrtcosPhi) - x0,
|
||
|
f1 = phi / (cosPhi_2 * cosLambda_6) - y0,
|
||
|
df0dPhi = sqrtcosPhi ? -0.25 * lambda * sinPhi / sqrtcosPhi : 0,
|
||
|
df0dLambda = 0.5 * (1 + sqrtcosPhi),
|
||
|
df1dPhi = (1 +0.5 * phi * sinPhi_2 / cosPhi_2) / (cosPhi_2 * cosLambda_6),
|
||
|
df1dLambda = (phi / cosPhi_2) * (sinLambda_6 / 6) / (cosLambda_6 * cosLambda_6),
|
||
|
denom = df0dPhi * df1dLambda - df1dPhi * df0dLambda,
|
||
|
dPhi = (f0 * df1dLambda - f1 * df0dLambda) / denom,
|
||
|
dLambda = (f1 * df0dPhi - f0 * df1dPhi) / denom;
|
||
|
phi -= dPhi;
|
||
|
lambda -= dLambda;
|
||
|
if (abs(dPhi) < epsilon && abs(dLambda) < epsilon) break;
|
||
|
}
|
||
|
return [x < 0 ? -lambda : lambda, y < 0 ? -phi : phi];
|
||
|
};
|
||
|
|
||
|
function larrivee() {
|
||
|
return d3Geo.geoProjection(larriveeRaw)
|
||
|
.scale(97.2672);
|
||
|
}
|
||
|
|
||
|
function laskowskiRaw(lambda, phi) {
|
||
|
var lambda2 = lambda * lambda, phi2 = phi * phi;
|
||
|
return [
|
||
|
lambda * (0.975534 + phi2 * (-0.119161 + lambda2 * -0.0143059 + phi2 * -0.0547009)),
|
||
|
phi * (1.00384 + lambda2 * (0.0802894 + phi2 * -0.02855 + lambda2 * 0.000199025) + phi2 * (0.0998909 + phi2 * -0.0491032))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
laskowskiRaw.invert = function(x, y) {
|
||
|
var lambda = sign(x) * pi,
|
||
|
phi = y / 2,
|
||
|
i = 50;
|
||
|
do {
|
||
|
var lambda2 = lambda * lambda,
|
||
|
phi2 = phi * phi,
|
||
|
lambdaPhi = lambda * phi,
|
||
|
fx = lambda * (0.975534 + phi2 * (-0.119161 + lambda2 * -0.0143059 + phi2 * -0.0547009)) - x,
|
||
|
fy = phi * (1.00384 + lambda2 * (0.0802894 + phi2 * -0.02855 + lambda2 * 0.000199025) + phi2 * (0.0998909 + phi2 * -0.0491032)) - y,
|
||
|
deltaxDeltaLambda = 0.975534 - phi2 * (0.119161 + 3 * lambda2 * 0.0143059 + phi2 * 0.0547009),
|
||
|
deltaxDeltaPhi = -lambdaPhi * (2 * 0.119161 + 4 * 0.0547009 * phi2 + 2 * 0.0143059 * lambda2),
|
||
|
deltayDeltaLambda = lambdaPhi * (2 * 0.0802894 + 4 * 0.000199025 * lambda2 + 2 * -0.02855 * phi2),
|
||
|
deltayDeltaPhi = 1.00384 + lambda2 * (0.0802894 + 0.000199025 * lambda2) + phi2 * (3 * (0.0998909 - 0.02855 * lambda2) - 5 * 0.0491032 * phi2),
|
||
|
denominator = deltaxDeltaPhi * deltayDeltaLambda - deltayDeltaPhi * deltaxDeltaLambda,
|
||
|
deltaLambda = (fy * deltaxDeltaPhi - fx * deltayDeltaPhi) / denominator,
|
||
|
deltaPhi = (fx * deltayDeltaLambda - fy * deltaxDeltaLambda) / denominator;
|
||
|
lambda -= deltaLambda, phi -= deltaPhi;
|
||
|
} while ((abs(deltaLambda) > epsilon || abs(deltaPhi) > epsilon) && --i > 0);
|
||
|
return i && [lambda, phi];
|
||
|
};
|
||
|
|
||
|
function laskowski() {
|
||
|
return d3Geo.geoProjection(laskowskiRaw)
|
||
|
.scale(139.98);
|
||
|
}
|
||
|
|
||
|
function littrowRaw(lambda, phi) {
|
||
|
return [
|
||
|
sin(lambda) / cos(phi),
|
||
|
tan(phi) * cos(lambda)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
littrowRaw.invert = function(x, y) {
|
||
|
var x2 = x * x,
|
||
|
y2 = y * y,
|
||
|
y2_1 = y2 + 1,
|
||
|
x2_y2_1 = x2 + y2_1,
|
||
|
cosPhi = x
|
||
|
? sqrt1_2 * sqrt((x2_y2_1 - sqrt(x2_y2_1 * x2_y2_1 - 4 * x2)) / x2)
|
||
|
: 1 / sqrt(y2_1);
|
||
|
return [
|
||
|
asin(x * cosPhi),
|
||
|
sign(y) * acos(cosPhi)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function littrow() {
|
||
|
return d3Geo.geoProjection(littrowRaw)
|
||
|
.scale(144.049)
|
||
|
.clipAngle(90 - 1e-3);
|
||
|
}
|
||
|
|
||
|
function loximuthalRaw(phi0) {
|
||
|
var cosPhi0 = cos(phi0),
|
||
|
tanPhi0 = tan(quarterPi + phi0 / 2);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var y = phi - phi0,
|
||
|
x = abs(y) < epsilon ? lambda * cosPhi0
|
||
|
: abs(x = quarterPi + phi / 2) < epsilon || abs(abs(x) - halfPi) < epsilon
|
||
|
? 0 : lambda * y / log(tan(x) / tanPhi0);
|
||
|
return [x, y];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var lambda,
|
||
|
phi = y + phi0;
|
||
|
return [
|
||
|
abs(y) < epsilon ? x / cosPhi0
|
||
|
: (abs(lambda = quarterPi + phi / 2) < epsilon || abs(abs(lambda) - halfPi) < epsilon) ? 0
|
||
|
: x * log(tan(lambda) / tanPhi0) / y,
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function loximuthal() {
|
||
|
return parallel1(loximuthalRaw)
|
||
|
.parallel(40)
|
||
|
.scale(158.837);
|
||
|
}
|
||
|
|
||
|
function millerRaw(lambda, phi) {
|
||
|
return [lambda, 1.25 * log(tan(quarterPi + 0.4 * phi))];
|
||
|
}
|
||
|
|
||
|
millerRaw.invert = function(x, y) {
|
||
|
return [x, 2.5 * atan(exp(0.8 * y)) - 0.625 * pi];
|
||
|
};
|
||
|
|
||
|
function miller() {
|
||
|
return d3Geo.geoProjection(millerRaw)
|
||
|
.scale(108.318);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographicRaw(C) {
|
||
|
var m = C.length - 1;
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var cosPhi = cos(phi),
|
||
|
k = 2 / (1 + cosPhi * cos(lambda)),
|
||
|
zr = k * cosPhi * sin(lambda),
|
||
|
zi = k * sin(phi),
|
||
|
i = m,
|
||
|
w = C[i],
|
||
|
ar = w[0],
|
||
|
ai = w[1],
|
||
|
t;
|
||
|
while (--i >= 0) {
|
||
|
w = C[i];
|
||
|
ar = w[0] + zr * (t = ar) - zi * ai;
|
||
|
ai = w[1] + zr * ai + zi * t;
|
||
|
}
|
||
|
ar = zr * (t = ar) - zi * ai;
|
||
|
ai = zr * ai + zi * t;
|
||
|
return [ar, ai];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var i = 20,
|
||
|
zr = x,
|
||
|
zi = y;
|
||
|
do {
|
||
|
var j = m,
|
||
|
w = C[j],
|
||
|
ar = w[0],
|
||
|
ai = w[1],
|
||
|
br = 0,
|
||
|
bi = 0,
|
||
|
t;
|
||
|
|
||
|
while (--j >= 0) {
|
||
|
w = C[j];
|
||
|
br = ar + zr * (t = br) - zi * bi;
|
||
|
bi = ai + zr * bi + zi * t;
|
||
|
ar = w[0] + zr * (t = ar) - zi * ai;
|
||
|
ai = w[1] + zr * ai + zi * t;
|
||
|
}
|
||
|
br = ar + zr * (t = br) - zi * bi;
|
||
|
bi = ai + zr * bi + zi * t;
|
||
|
ar = zr * (t = ar) - zi * ai - x;
|
||
|
ai = zr * ai + zi * t - y;
|
||
|
|
||
|
var denominator = br * br + bi * bi, deltar, deltai;
|
||
|
zr -= deltar = (ar * br + ai * bi) / denominator;
|
||
|
zi -= deltai = (ai * br - ar * bi) / denominator;
|
||
|
} while (abs(deltar) + abs(deltai) > epsilon * epsilon && --i > 0);
|
||
|
|
||
|
if (i) {
|
||
|
var rho = sqrt(zr * zr + zi * zi),
|
||
|
c = 2 * atan(rho * 0.5),
|
||
|
sinc = sin(c);
|
||
|
return [atan2(zr * sinc, rho * cos(c)), rho ? asin(zi * sinc / rho) : 0];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
var alaska = [[0.9972523, 0], [0.0052513, -0.0041175], [0.0074606, 0.0048125], [-0.0153783, -0.1968253], [0.0636871, -0.1408027], [0.3660976, -0.2937382]],
|
||
|
gs48 = [[0.98879, 0], [0, 0], [-0.050909, 0], [0, 0], [0.075528, 0]],
|
||
|
gs50 = [[0.9842990, 0], [0.0211642, 0.0037608], [-0.1036018, -0.0575102], [-0.0329095, -0.0320119], [0.0499471, 0.1223335], [0.0260460, 0.0899805], [0.0007388, -0.1435792], [0.0075848, -0.1334108], [-0.0216473, 0.0776645], [-0.0225161, 0.0853673]],
|
||
|
miller$1 = [[0.9245, 0], [0, 0], [0.01943, 0]],
|
||
|
lee = [[0.721316, 0], [0, 0], [-0.00881625, -0.00617325]];
|
||
|
|
||
|
function modifiedStereographicAlaska() {
|
||
|
return modifiedStereographic(alaska, [152, -64])
|
||
|
.scale(1400)
|
||
|
.center([-160.908, 62.4864])
|
||
|
.clipAngle(30)
|
||
|
.angle(7.8);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographicGs48() {
|
||
|
return modifiedStereographic(gs48, [95, -38])
|
||
|
.scale(1000)
|
||
|
.clipAngle(55)
|
||
|
.center([-96.5563, 38.8675]);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographicGs50() {
|
||
|
return modifiedStereographic(gs50, [120, -45])
|
||
|
.scale(359.513)
|
||
|
.clipAngle(55)
|
||
|
.center([-117.474, 53.0628]);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographicMiller() {
|
||
|
return modifiedStereographic(miller$1, [-20, -18])
|
||
|
.scale(209.091)
|
||
|
.center([20, 16.7214])
|
||
|
.clipAngle(82);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographicLee() {
|
||
|
return modifiedStereographic(lee, [165, 10])
|
||
|
.scale(250)
|
||
|
.clipAngle(130)
|
||
|
.center([-165, -10]);
|
||
|
}
|
||
|
|
||
|
function modifiedStereographic(coefficients, rotate) {
|
||
|
var p = d3Geo.geoProjection(modifiedStereographicRaw(coefficients)).rotate(rotate).clipAngle(90),
|
||
|
r = d3Geo.geoRotation(rotate),
|
||
|
center = p.center;
|
||
|
|
||
|
delete p.rotate;
|
||
|
|
||
|
p.center = function(_) {
|
||
|
return arguments.length ? center(r(_)) : r.invert(center());
|
||
|
};
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
var sqrt6 = sqrt(6),
|
||
|
sqrt7 = sqrt(7);
|
||
|
|
||
|
function mtFlatPolarParabolicRaw(lambda, phi) {
|
||
|
var theta = asin(7 * sin(phi) / (3 * sqrt6));
|
||
|
return [
|
||
|
sqrt6 * lambda * (2 * cos(2 * theta / 3) - 1) / sqrt7,
|
||
|
9 * sin(theta / 3) / sqrt7
|
||
|
];
|
||
|
}
|
||
|
|
||
|
mtFlatPolarParabolicRaw.invert = function(x, y) {
|
||
|
var theta = 3 * asin(y * sqrt7 / 9);
|
||
|
return [
|
||
|
x * sqrt7 / (sqrt6 * (2 * cos(2 * theta / 3) - 1)),
|
||
|
asin(sin(theta) * 3 * sqrt6 / 7)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function mtFlatPolarParabolic() {
|
||
|
return d3Geo.geoProjection(mtFlatPolarParabolicRaw)
|
||
|
.scale(164.859);
|
||
|
}
|
||
|
|
||
|
function mtFlatPolarQuarticRaw(lambda, phi) {
|
||
|
var k = (1 + sqrt1_2) * sin(phi),
|
||
|
theta = phi;
|
||
|
for (var i = 0, delta; i < 25; i++) {
|
||
|
theta -= delta = (sin(theta / 2) + sin(theta) - k) / (0.5 * cos(theta / 2) + cos(theta));
|
||
|
if (abs(delta) < epsilon) break;
|
||
|
}
|
||
|
return [
|
||
|
lambda * (1 + 2 * cos(theta) / cos(theta / 2)) / (3 * sqrt2),
|
||
|
2 * sqrt(3) * sin(theta / 2) / sqrt(2 + sqrt2)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
mtFlatPolarQuarticRaw.invert = function(x, y) {
|
||
|
var sinTheta_2 = y * sqrt(2 + sqrt2) / (2 * sqrt(3)),
|
||
|
theta = 2 * asin(sinTheta_2);
|
||
|
return [
|
||
|
3 * sqrt2 * x / (1 + 2 * cos(theta) / cos(theta / 2)),
|
||
|
asin((sinTheta_2 + sin(theta)) / (1 + sqrt1_2))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function mtFlatPolarQuartic() {
|
||
|
return d3Geo.geoProjection(mtFlatPolarQuarticRaw)
|
||
|
.scale(188.209);
|
||
|
}
|
||
|
|
||
|
function mtFlatPolarSinusoidalRaw(lambda, phi) {
|
||
|
var A = sqrt(6 / (4 + pi)),
|
||
|
k = (1 + pi / 4) * sin(phi),
|
||
|
theta = phi / 2;
|
||
|
for (var i = 0, delta; i < 25; i++) {
|
||
|
theta -= delta = (theta / 2 + sin(theta) - k) / (0.5 + cos(theta));
|
||
|
if (abs(delta) < epsilon) break;
|
||
|
}
|
||
|
return [
|
||
|
A * (0.5 + cos(theta)) * lambda / 1.5,
|
||
|
A * theta
|
||
|
];
|
||
|
}
|
||
|
|
||
|
mtFlatPolarSinusoidalRaw.invert = function(x, y) {
|
||
|
var A = sqrt(6 / (4 + pi)),
|
||
|
theta = y / A;
|
||
|
if (abs(abs(theta) - halfPi) < epsilon) theta = theta < 0 ? -halfPi : halfPi;
|
||
|
return [
|
||
|
1.5 * x / (A * (0.5 + cos(theta))),
|
||
|
asin((theta / 2 + sin(theta)) / (1 + pi / 4))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function mtFlatPolarSinusoidal() {
|
||
|
return d3Geo.geoProjection(mtFlatPolarSinusoidalRaw)
|
||
|
.scale(166.518);
|
||
|
}
|
||
|
|
||
|
function naturalEarth2Raw(lambda, phi) {
|
||
|
var phi2 = phi * phi, phi4 = phi2 * phi2, phi6 = phi2 * phi4;
|
||
|
return [
|
||
|
lambda * (0.84719 - 0.13063 * phi2 + phi6 * phi6 * (-0.04515 + 0.05494 * phi2 - 0.02326 * phi4 + 0.00331 * phi6)),
|
||
|
phi * (1.01183 + phi4 * phi4 * (-0.02625 + 0.01926 * phi2 - 0.00396 * phi4))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
naturalEarth2Raw.invert = function(x, y) {
|
||
|
var phi = y, i = 25, delta, phi2, phi4, phi6;
|
||
|
do {
|
||
|
phi2 = phi * phi; phi4 = phi2 * phi2;
|
||
|
phi -= delta = ((phi * (1.01183 + phi4 * phi4 * (-0.02625 + 0.01926 * phi2 - 0.00396 * phi4))) - y) /
|
||
|
(1.01183 + phi4 * phi4 * ((9 * -0.02625) + (11 * 0.01926) * phi2 + (13 * -0.00396) * phi4));
|
||
|
} while (abs(delta) > epsilon2 && --i > 0);
|
||
|
phi2 = phi * phi; phi4 = phi2 * phi2; phi6 = phi2 * phi4;
|
||
|
return [
|
||
|
x / (0.84719 - 0.13063 * phi2 + phi6 * phi6 * (-0.04515 + 0.05494 * phi2 - 0.02326 * phi4 + 0.00331 * phi6)),
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function naturalEarth2() {
|
||
|
return d3Geo.geoProjection(naturalEarth2Raw)
|
||
|
.scale(175.295);
|
||
|
}
|
||
|
|
||
|
function nellHammerRaw(lambda, phi) {
|
||
|
return [
|
||
|
lambda * (1 + cos(phi)) / 2,
|
||
|
2 * (phi - tan(phi / 2))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
nellHammerRaw.invert = function(x, y) {
|
||
|
var p = y / 2;
|
||
|
for (var i = 0, delta = Infinity; i < 10 && abs(delta) > epsilon; ++i) {
|
||
|
var c = cos(y / 2);
|
||
|
y -= delta = (y - tan(y / 2) - p) / (1 - 0.5 / (c * c));
|
||
|
}
|
||
|
return [
|
||
|
2 * x / (1 + cos(y)),
|
||
|
y
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function nellHammer() {
|
||
|
return d3Geo.geoProjection(nellHammerRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
var lobes$6 = [[ // northern hemisphere
|
||
|
[[-180, 0], [-90, 90], [ 0, 0]],
|
||
|
[[ 0, 0], [ 90, 90], [ 180, 0]]
|
||
|
], [ // southern hemisphere
|
||
|
[[-180, 0], [-90, -90], [ 0, 0]],
|
||
|
[[ 0, 0], [ 90, -90], [180, 0]]
|
||
|
]];
|
||
|
|
||
|
function quarticAuthalic() {
|
||
|
return interrupt(hammerRaw(Infinity), lobes$6)
|
||
|
.rotate([20, 0])
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
// Based on Torben Jansen's implementation
|
||
|
// https://beta.observablehq.com/@toja/nicolosi-globular-projection
|
||
|
// https://beta.observablehq.com/@toja/nicolosi-globular-inverse
|
||
|
|
||
|
function nicolosiRaw(lambda, phi) {
|
||
|
var sinPhi = sin(phi),
|
||
|
q = cos(phi),
|
||
|
s = sign(lambda);
|
||
|
|
||
|
if (lambda === 0 || abs(phi) === halfPi) return [0, phi];
|
||
|
else if (phi === 0) return [lambda, 0];
|
||
|
else if (abs(lambda) === halfPi) return [lambda * q, halfPi * sinPhi];
|
||
|
|
||
|
var b = pi / (2 * lambda) - (2 * lambda) / pi,
|
||
|
c = (2 * phi) / pi,
|
||
|
d = (1 - c * c) / (sinPhi - c);
|
||
|
|
||
|
var b2 = b * b,
|
||
|
d2 = d * d,
|
||
|
b2d2 = 1 + b2 / d2,
|
||
|
d2b2 = 1 + d2 / b2;
|
||
|
|
||
|
var M = ((b * sinPhi) / d - b / 2) / b2d2,
|
||
|
N = ((d2 * sinPhi) / b2 + d / 2) / d2b2,
|
||
|
m = M * M + (q * q) / b2d2,
|
||
|
n = N * N - ((d2 * sinPhi * sinPhi) / b2 + d * sinPhi - 1) / d2b2;
|
||
|
|
||
|
return [
|
||
|
halfPi * (M + sqrt(m) * s),
|
||
|
halfPi * (N + sqrt(n < 0 ? 0 : n) * sign(-phi * b) * s)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
nicolosiRaw.invert = function(x, y) {
|
||
|
|
||
|
x /= halfPi;
|
||
|
y /= halfPi;
|
||
|
|
||
|
var x2 = x * x,
|
||
|
y2 = y * y,
|
||
|
x2y2 = x2 + y2,
|
||
|
pi2 = pi * pi;
|
||
|
|
||
|
return [
|
||
|
x ? (x2y2 -1 + sqrt((1 - x2y2) * (1 - x2y2) + 4 * x2)) / (2 * x) * halfPi : 0,
|
||
|
solve(function(phi) {
|
||
|
return (
|
||
|
x2y2 * (pi * sin(phi) - 2 * phi) * pi +
|
||
|
4 * phi * phi * (y - sin(phi)) +
|
||
|
2 * pi * phi -
|
||
|
pi2 * y
|
||
|
);
|
||
|
}, 0)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function nicolosi() {
|
||
|
return d3Geo.geoProjection(nicolosiRaw)
|
||
|
.scale(127.267);
|
||
|
}
|
||
|
|
||
|
// Based on Java implementation by Bojan Savric.
|
||
|
// https://github.com/OSUCartography/JMapProjLib/blob/master/src/com/jhlabs/map/proj/PattersonProjection.java
|
||
|
|
||
|
var pattersonK1 = 1.0148,
|
||
|
pattersonK2 = 0.23185,
|
||
|
pattersonK3 = -0.14499,
|
||
|
pattersonK4 = 0.02406,
|
||
|
pattersonC1 = pattersonK1,
|
||
|
pattersonC2 = 5 * pattersonK2,
|
||
|
pattersonC3 = 7 * pattersonK3,
|
||
|
pattersonC4 = 9 * pattersonK4,
|
||
|
pattersonYmax = 1.790857183;
|
||
|
|
||
|
function pattersonRaw(lambda, phi) {
|
||
|
var phi2 = phi * phi;
|
||
|
return [
|
||
|
lambda,
|
||
|
phi * (pattersonK1 + phi2 * phi2 * (pattersonK2 + phi2 * (pattersonK3 + pattersonK4 * phi2)))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
pattersonRaw.invert = function(x, y) {
|
||
|
if (y > pattersonYmax) y = pattersonYmax;
|
||
|
else if (y < -pattersonYmax) y = -pattersonYmax;
|
||
|
var yc = y, delta;
|
||
|
|
||
|
do { // Newton-Raphson
|
||
|
var y2 = yc * yc;
|
||
|
yc -= delta = ((yc * (pattersonK1 + y2 * y2 * (pattersonK2 + y2 * (pattersonK3 + pattersonK4 * y2)))) - y) / (pattersonC1 + y2 * y2 * (pattersonC2 + y2 * (pattersonC3 + pattersonC4 * y2)));
|
||
|
} while (abs(delta) > epsilon);
|
||
|
|
||
|
return [x, yc];
|
||
|
};
|
||
|
|
||
|
function patterson() {
|
||
|
return d3Geo.geoProjection(pattersonRaw)
|
||
|
.scale(139.319);
|
||
|
}
|
||
|
|
||
|
function polyconicRaw(lambda, phi) {
|
||
|
if (abs(phi) < epsilon) return [lambda, 0];
|
||
|
var tanPhi = tan(phi),
|
||
|
k = lambda * sin(phi);
|
||
|
return [
|
||
|
sin(k) / tanPhi,
|
||
|
phi + (1 - cos(k)) / tanPhi
|
||
|
];
|
||
|
}
|
||
|
|
||
|
polyconicRaw.invert = function(x, y) {
|
||
|
if (abs(y) < epsilon) return [x, 0];
|
||
|
var k = x * x + y * y,
|
||
|
phi = y * 0.5,
|
||
|
i = 10, delta;
|
||
|
do {
|
||
|
var tanPhi = tan(phi),
|
||
|
secPhi = 1 / cos(phi),
|
||
|
j = k - 2 * y * phi + phi * phi;
|
||
|
phi -= delta = (tanPhi * j + 2 * (phi - y)) / (2 + j * secPhi * secPhi + 2 * (phi - y) * tanPhi);
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
tanPhi = tan(phi);
|
||
|
return [
|
||
|
(abs(y) < abs(phi + 1 / tanPhi) ? asin(x * tanPhi) : sign(x) * (acos(abs(x * tanPhi)) + halfPi)) / sin(phi),
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function polyconic() {
|
||
|
return d3Geo.geoProjection(polyconicRaw)
|
||
|
.scale(103.74);
|
||
|
}
|
||
|
|
||
|
// Note: 6-element arrays are used to denote the 3x3 affine transform matrix:
|
||
|
// [a, b, c,
|
||
|
// d, e, f,
|
||
|
// 0, 0, 1] - this redundant row is left out.
|
||
|
|
||
|
// Transform matrix for [a0, a1] -> [b0, b1].
|
||
|
function matrix(a, b) {
|
||
|
var u = subtract(a[1], a[0]),
|
||
|
v = subtract(b[1], b[0]),
|
||
|
phi = angle$1(u, v),
|
||
|
s = length(u) / length(v);
|
||
|
|
||
|
return multiply([
|
||
|
1, 0, a[0][0],
|
||
|
0, 1, a[0][1]
|
||
|
], multiply([
|
||
|
s, 0, 0,
|
||
|
0, s, 0
|
||
|
], multiply([
|
||
|
cos(phi), sin(phi), 0,
|
||
|
-sin(phi), cos(phi), 0
|
||
|
], [
|
||
|
1, 0, -b[0][0],
|
||
|
0, 1, -b[0][1]
|
||
|
])));
|
||
|
}
|
||
|
|
||
|
// Inverts a transform matrix.
|
||
|
function inverse(m) {
|
||
|
var k = 1 / (m[0] * m[4] - m[1] * m[3]);
|
||
|
return [
|
||
|
k * m[4], -k * m[1], k * (m[1] * m[5] - m[2] * m[4]),
|
||
|
-k * m[3], k * m[0], k * (m[2] * m[3] - m[0] * m[5])
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Multiplies two 3x2 matrices.
|
||
|
function multiply(a, b) {
|
||
|
return [
|
||
|
a[0] * b[0] + a[1] * b[3],
|
||
|
a[0] * b[1] + a[1] * b[4],
|
||
|
a[0] * b[2] + a[1] * b[5] + a[2],
|
||
|
a[3] * b[0] + a[4] * b[3],
|
||
|
a[3] * b[1] + a[4] * b[4],
|
||
|
a[3] * b[2] + a[4] * b[5] + a[5]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Subtracts 2D vectors.
|
||
|
function subtract(a, b) {
|
||
|
return [a[0] - b[0], a[1] - b[1]];
|
||
|
}
|
||
|
|
||
|
// Magnitude of a 2D vector.
|
||
|
function length(v) {
|
||
|
return sqrt(v[0] * v[0] + v[1] * v[1]);
|
||
|
}
|
||
|
|
||
|
// Angle between two 2D vectors.
|
||
|
function angle$1(a, b) {
|
||
|
return atan2(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]);
|
||
|
}
|
||
|
|
||
|
// Creates a polyhedral projection.
|
||
|
// * root: a spanning tree of polygon faces. Nodes are automatically
|
||
|
// augmented with a transform matrix.
|
||
|
// * face: a function that returns the appropriate node for a given {lambda, phi}
|
||
|
// point (radians).
|
||
|
// * r: rotation angle for root face [deprecated by .angle()].
|
||
|
function polyhedral(root, face, r) {
|
||
|
|
||
|
recurse(root, {transform: null});
|
||
|
|
||
|
function recurse(node, parent) {
|
||
|
node.edges = faceEdges(node.face);
|
||
|
// Find shared edge.
|
||
|
if (parent.face) {
|
||
|
var shared = node.shared = sharedEdge(node.face, parent.face),
|
||
|
m = matrix(shared.map(parent.project), shared.map(node.project));
|
||
|
node.transform = parent.transform ? multiply(parent.transform, m) : m;
|
||
|
// Replace shared edge in parent edges array.
|
||
|
var edges = parent.edges;
|
||
|
for (var i = 0, n = edges.length; i < n; ++i) {
|
||
|
if (pointEqual$1(shared[0], edges[i][1]) && pointEqual$1(shared[1], edges[i][0])) edges[i] = node;
|
||
|
if (pointEqual$1(shared[0], edges[i][0]) && pointEqual$1(shared[1], edges[i][1])) edges[i] = node;
|
||
|
}
|
||
|
edges = node.edges;
|
||
|
for (i = 0, n = edges.length; i < n; ++i) {
|
||
|
if (pointEqual$1(shared[0], edges[i][0]) && pointEqual$1(shared[1], edges[i][1])) edges[i] = parent;
|
||
|
if (pointEqual$1(shared[0], edges[i][1]) && pointEqual$1(shared[1], edges[i][0])) edges[i] = parent;
|
||
|
}
|
||
|
} else {
|
||
|
node.transform = parent.transform;
|
||
|
}
|
||
|
if (node.children) {
|
||
|
node.children.forEach(function(child) {
|
||
|
recurse(child, node);
|
||
|
});
|
||
|
}
|
||
|
return node;
|
||
|
}
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var node = face(lambda, phi),
|
||
|
point = node.project([lambda * degrees, phi * degrees]),
|
||
|
t;
|
||
|
if (t = node.transform) {
|
||
|
return [
|
||
|
t[0] * point[0] + t[1] * point[1] + t[2],
|
||
|
-(t[3] * point[0] + t[4] * point[1] + t[5])
|
||
|
];
|
||
|
}
|
||
|
point[1] = -point[1];
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
// Naive inverse! A faster solution would use bounding boxes, or even a
|
||
|
// polygonal quadtree.
|
||
|
if (hasInverse(root)) forward.invert = function(x, y) {
|
||
|
var coordinates = faceInvert(root, [x, -y]);
|
||
|
return coordinates && (coordinates[0] *= radians, coordinates[1] *= radians, coordinates);
|
||
|
};
|
||
|
|
||
|
function faceInvert(node, coordinates) {
|
||
|
var invert = node.project.invert,
|
||
|
t = node.transform,
|
||
|
point = coordinates;
|
||
|
if (t) {
|
||
|
t = inverse(t);
|
||
|
point = [
|
||
|
t[0] * point[0] + t[1] * point[1] + t[2],
|
||
|
(t[3] * point[0] + t[4] * point[1] + t[5])
|
||
|
];
|
||
|
}
|
||
|
if (invert && node === faceDegrees(p = invert(point))) return p;
|
||
|
var p,
|
||
|
children = node.children;
|
||
|
for (var i = 0, n = children && children.length; i < n; ++i) {
|
||
|
if (p = faceInvert(children[i], coordinates)) return p;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function faceDegrees(coordinates) {
|
||
|
return face(coordinates[0] * radians, coordinates[1] * radians);
|
||
|
}
|
||
|
|
||
|
var proj = d3Geo.geoProjection(forward),
|
||
|
stream_ = proj.stream;
|
||
|
|
||
|
proj.stream = function(stream) {
|
||
|
var rotate = proj.rotate(),
|
||
|
rotateStream = stream_(stream),
|
||
|
sphereStream = (proj.rotate([0, 0]), stream_(stream));
|
||
|
proj.rotate(rotate);
|
||
|
rotateStream.sphere = function() {
|
||
|
sphereStream.polygonStart();
|
||
|
sphereStream.lineStart();
|
||
|
outline(sphereStream, root);
|
||
|
sphereStream.lineEnd();
|
||
|
sphereStream.polygonEnd();
|
||
|
};
|
||
|
return rotateStream;
|
||
|
};
|
||
|
|
||
|
return proj.angle(r == null ? -30 : r * degrees);
|
||
|
}
|
||
|
|
||
|
function outline(stream, node, parent) {
|
||
|
var point,
|
||
|
edges = node.edges,
|
||
|
n = edges.length,
|
||
|
edge,
|
||
|
multiPoint = {type: "MultiPoint", coordinates: node.face},
|
||
|
notPoles = node.face.filter(function(d) { return abs(d[1]) !== 90; }),
|
||
|
b = d3Geo.geoBounds({type: "MultiPoint", coordinates: notPoles}),
|
||
|
inside = false,
|
||
|
j = -1,
|
||
|
dx = b[1][0] - b[0][0];
|
||
|
// TODO
|
||
|
var c = dx === 180 || dx === 360
|
||
|
? [(b[0][0] + b[1][0]) / 2, (b[0][1] + b[1][1]) / 2]
|
||
|
: d3Geo.geoCentroid(multiPoint);
|
||
|
// First find the shared edge…
|
||
|
if (parent) while (++j < n) {
|
||
|
if (edges[j] === parent) break;
|
||
|
}
|
||
|
++j;
|
||
|
for (var i = 0; i < n; ++i) {
|
||
|
edge = edges[(i + j) % n];
|
||
|
if (Array.isArray(edge)) {
|
||
|
if (!inside) {
|
||
|
stream.point((point = d3Geo.geoInterpolate(edge[0], c)(epsilon))[0], point[1]);
|
||
|
inside = true;
|
||
|
}
|
||
|
stream.point((point = d3Geo.geoInterpolate(edge[1], c)(epsilon))[0], point[1]);
|
||
|
} else {
|
||
|
inside = false;
|
||
|
if (edge !== parent) outline(stream, edge, node);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tests equality of two spherical points.
|
||
|
function pointEqual$1(a, b) {
|
||
|
return a && b && a[0] === b[0] && a[1] === b[1];
|
||
|
}
|
||
|
|
||
|
// Finds a shared edge given two clockwise polygons.
|
||
|
function sharedEdge(a, b) {
|
||
|
var x, y, n = a.length, found = null;
|
||
|
for (var i = 0; i < n; ++i) {
|
||
|
x = a[i];
|
||
|
for (var j = b.length; --j >= 0;) {
|
||
|
y = b[j];
|
||
|
if (x[0] === y[0] && x[1] === y[1]) {
|
||
|
if (found) return [found, x];
|
||
|
found = x;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Converts an array of n face vertices to an array of n + 1 edges.
|
||
|
function faceEdges(face) {
|
||
|
var n = face.length,
|
||
|
edges = [];
|
||
|
for (var a = face[n - 1], i = 0; i < n; ++i) edges.push([a, a = face[i]]);
|
||
|
return edges;
|
||
|
}
|
||
|
|
||
|
function hasInverse(node) {
|
||
|
return node.project.invert || node.children && node.children.some(hasInverse);
|
||
|
}
|
||
|
|
||
|
// TODO generate on-the-fly to avoid external modification.
|
||
|
var octahedron = [
|
||
|
[0, 90],
|
||
|
[-90, 0], [0, 0], [90, 0], [180, 0],
|
||
|
[0, -90]
|
||
|
];
|
||
|
|
||
|
var octahedron$1 = [
|
||
|
[0, 2, 1],
|
||
|
[0, 3, 2],
|
||
|
[5, 1, 2],
|
||
|
[5, 2, 3],
|
||
|
[0, 1, 4],
|
||
|
[0, 4, 3],
|
||
|
[5, 4, 1],
|
||
|
[5, 3, 4]
|
||
|
].map(function(face) {
|
||
|
return face.map(function(i) {
|
||
|
return octahedron[i];
|
||
|
});
|
||
|
});
|
||
|
|
||
|
function butterfly(faceProjection) {
|
||
|
|
||
|
faceProjection = faceProjection || function(face) {
|
||
|
var c = d3Geo.geoCentroid({type: "MultiPoint", coordinates: face});
|
||
|
return d3Geo.geoGnomonic().scale(1).translate([0, 0]).rotate([-c[0], -c[1]]);
|
||
|
};
|
||
|
|
||
|
var faces = octahedron$1.map(function(face) {
|
||
|
return {face: face, project: faceProjection(face)};
|
||
|
});
|
||
|
|
||
|
[-1, 0, 0, 1, 0, 1, 4, 5].forEach(function(d, i) {
|
||
|
var node = faces[d];
|
||
|
node && (node.children || (node.children = [])).push(faces[i]);
|
||
|
});
|
||
|
|
||
|
return polyhedral(faces[0], function(lambda, phi) {
|
||
|
return faces[lambda < -pi / 2 ? phi < 0 ? 6 : 4
|
||
|
: lambda < 0 ? phi < 0 ? 2 : 0
|
||
|
: lambda < pi / 2 ? phi < 0 ? 3 : 1
|
||
|
: phi < 0 ? 7 : 5];
|
||
|
})
|
||
|
.angle(-30)
|
||
|
.scale(101.858)
|
||
|
.center([0, 45]);
|
||
|
}
|
||
|
|
||
|
var kx = 2 / sqrt(3);
|
||
|
|
||
|
function collignonK(a, b) {
|
||
|
var p = collignonRaw(a, b);
|
||
|
return [p[0] * kx, p[1]];
|
||
|
}
|
||
|
|
||
|
collignonK.invert = function(x,y) {
|
||
|
return collignonRaw.invert(x / kx, y);
|
||
|
};
|
||
|
|
||
|
function collignon$1(faceProjection) {
|
||
|
|
||
|
faceProjection = faceProjection || function(face) {
|
||
|
var c = d3Geo.geoCentroid({type: "MultiPoint", coordinates: face});
|
||
|
return d3Geo.geoProjection(collignonK).translate([0, 0]).scale(1).rotate(c[1] > 0 ? [-c[0], 0] : [180 - c[0], 180]);
|
||
|
};
|
||
|
|
||
|
var faces = octahedron$1.map(function(face) {
|
||
|
return {face: face, project: faceProjection(face)};
|
||
|
});
|
||
|
|
||
|
[-1, 0, 0, 1, 0, 1, 4, 5].forEach(function(d, i) {
|
||
|
var node = faces[d];
|
||
|
node && (node.children || (node.children = [])).push(faces[i]);
|
||
|
});
|
||
|
|
||
|
return polyhedral(faces[0], function(lambda, phi) {
|
||
|
return faces[lambda < -pi / 2 ? phi < 0 ? 6 : 4
|
||
|
: lambda < 0 ? phi < 0 ? 2 : 0
|
||
|
: lambda < pi / 2 ? phi < 0 ? 3 : 1
|
||
|
: phi < 0 ? 7 : 5];
|
||
|
})
|
||
|
.angle(-30)
|
||
|
.scale(121.906)
|
||
|
.center([0, 48.5904]);
|
||
|
}
|
||
|
|
||
|
function waterman(faceProjection) {
|
||
|
|
||
|
faceProjection = faceProjection || function(face) {
|
||
|
var c = face.length === 6 ? d3Geo.geoCentroid({type: "MultiPoint", coordinates: face}) : face[0];
|
||
|
return d3Geo.geoGnomonic().scale(1).translate([0, 0]).rotate([-c[0], -c[1]]);
|
||
|
};
|
||
|
|
||
|
var w5 = octahedron$1.map(function(face) {
|
||
|
var xyz = face.map(cartesian),
|
||
|
n = xyz.length,
|
||
|
a = xyz[n - 1],
|
||
|
b,
|
||
|
hexagon = [];
|
||
|
for (var i = 0; i < n; ++i) {
|
||
|
b = xyz[i];
|
||
|
hexagon.push(spherical([
|
||
|
a[0] * 0.9486832980505138 + b[0] * 0.31622776601683794,
|
||
|
a[1] * 0.9486832980505138 + b[1] * 0.31622776601683794,
|
||
|
a[2] * 0.9486832980505138 + b[2] * 0.31622776601683794
|
||
|
]), spherical([
|
||
|
b[0] * 0.9486832980505138 + a[0] * 0.31622776601683794,
|
||
|
b[1] * 0.9486832980505138 + a[1] * 0.31622776601683794,
|
||
|
b[2] * 0.9486832980505138 + a[2] * 0.31622776601683794
|
||
|
]));
|
||
|
a = b;
|
||
|
}
|
||
|
return hexagon;
|
||
|
});
|
||
|
|
||
|
var cornerNormals = [];
|
||
|
|
||
|
var parents = [-1, 0, 0, 1, 0, 1, 4, 5];
|
||
|
|
||
|
w5.forEach(function(hexagon, j) {
|
||
|
var face = octahedron$1[j],
|
||
|
n = face.length,
|
||
|
normals = cornerNormals[j] = [];
|
||
|
for (var i = 0; i < n; ++i) {
|
||
|
w5.push([
|
||
|
face[i],
|
||
|
hexagon[(i * 2 + 2) % (2 * n)],
|
||
|
hexagon[(i * 2 + 1) % (2 * n)]
|
||
|
]);
|
||
|
parents.push(j);
|
||
|
normals.push(cross(
|
||
|
cartesian(hexagon[(i * 2 + 2) % (2 * n)]),
|
||
|
cartesian(hexagon[(i * 2 + 1) % (2 * n)])
|
||
|
));
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var faces = w5.map(function(face) {
|
||
|
return {
|
||
|
project: faceProjection(face),
|
||
|
face: face
|
||
|
};
|
||
|
});
|
||
|
|
||
|
parents.forEach(function(d, i) {
|
||
|
var parent = faces[d];
|
||
|
parent && (parent.children || (parent.children = [])).push(faces[i]);
|
||
|
});
|
||
|
|
||
|
function face(lambda, phi) {
|
||
|
var cosphi = cos(phi),
|
||
|
p = [cosphi * cos(lambda), cosphi * sin(lambda), sin(phi)];
|
||
|
|
||
|
var hexagon = lambda < -pi / 2 ? phi < 0 ? 6 : 4
|
||
|
: lambda < 0 ? phi < 0 ? 2 : 0
|
||
|
: lambda < pi / 2 ? phi < 0 ? 3 : 1
|
||
|
: phi < 0 ? 7 : 5;
|
||
|
|
||
|
var n = cornerNormals[hexagon];
|
||
|
|
||
|
return faces[dot(n[0], p) < 0 ? 8 + 3 * hexagon
|
||
|
: dot(n[1], p) < 0 ? 8 + 3 * hexagon + 1
|
||
|
: dot(n[2], p) < 0 ? 8 + 3 * hexagon + 2
|
||
|
: hexagon];
|
||
|
}
|
||
|
|
||
|
return polyhedral(faces[0], face)
|
||
|
.angle(-30)
|
||
|
.scale(110.625)
|
||
|
.center([0,45]);
|
||
|
}
|
||
|
|
||
|
function dot(a, b) {
|
||
|
for (var i = 0, n = a.length, s = 0; i < n; ++i) s += a[i] * b[i];
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
function cross(a, b) {
|
||
|
return [
|
||
|
a[1] * b[2] - a[2] * b[1],
|
||
|
a[2] * b[0] - a[0] * b[2],
|
||
|
a[0] * b[1] - a[1] * b[0]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Converts 3D Cartesian to spherical coordinates (degrees).
|
||
|
function spherical(cartesian) {
|
||
|
return [
|
||
|
atan2(cartesian[1], cartesian[0]) * degrees,
|
||
|
asin(max(-1, min(1, cartesian[2]))) * degrees
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// Converts spherical coordinates (degrees) to 3D Cartesian.
|
||
|
function cartesian(coordinates) {
|
||
|
var lambda = coordinates[0] * radians,
|
||
|
phi = coordinates[1] * radians,
|
||
|
cosphi = cos(phi);
|
||
|
return [
|
||
|
cosphi * cos(lambda),
|
||
|
cosphi * sin(lambda),
|
||
|
sin(phi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function noop() {}
|
||
|
|
||
|
function clockwise(ring) {
|
||
|
if ((n = ring.length) < 4) return false;
|
||
|
var i = 0,
|
||
|
n,
|
||
|
area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
|
||
|
while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
|
||
|
return area <= 0;
|
||
|
}
|
||
|
|
||
|
function contains(ring, point) {
|
||
|
var x = point[0],
|
||
|
y = point[1],
|
||
|
contains = false;
|
||
|
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
|
||
|
var pi = ring[i], xi = pi[0], yi = pi[1],
|
||
|
pj = ring[j], xj = pj[0], yj = pj[1];
|
||
|
if (((yi > y) ^ (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) contains = !contains;
|
||
|
}
|
||
|
return contains;
|
||
|
}
|
||
|
|
||
|
function index(object, projection) {
|
||
|
var stream = projection.stream, project;
|
||
|
if (!stream) throw new Error("invalid projection");
|
||
|
switch (object && object.type) {
|
||
|
case "Feature": project = projectFeature; break;
|
||
|
case "FeatureCollection": project = projectFeatureCollection; break;
|
||
|
default: project = projectGeometry; break;
|
||
|
}
|
||
|
return project(object, stream);
|
||
|
}
|
||
|
|
||
|
function projectFeatureCollection(o, stream) {
|
||
|
return {
|
||
|
type: "FeatureCollection",
|
||
|
features: o.features.map(function(f) {
|
||
|
return projectFeature(f, stream);
|
||
|
})
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function projectFeature(o, stream) {
|
||
|
return {
|
||
|
type: "Feature",
|
||
|
id: o.id,
|
||
|
properties: o.properties,
|
||
|
geometry: projectGeometry(o.geometry, stream)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function projectGeometryCollection(o, stream) {
|
||
|
return {
|
||
|
type: "GeometryCollection",
|
||
|
geometries: o.geometries.map(function(o) {
|
||
|
return projectGeometry(o, stream);
|
||
|
})
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function projectGeometry(o, stream) {
|
||
|
if (!o) return null;
|
||
|
if (o.type === "GeometryCollection") return projectGeometryCollection(o, stream);
|
||
|
var sink;
|
||
|
switch (o.type) {
|
||
|
case "Point": sink = sinkPoint; break;
|
||
|
case "MultiPoint": sink = sinkPoint; break;
|
||
|
case "LineString": sink = sinkLine; break;
|
||
|
case "MultiLineString": sink = sinkLine; break;
|
||
|
case "Polygon": sink = sinkPolygon; break;
|
||
|
case "MultiPolygon": sink = sinkPolygon; break;
|
||
|
case "Sphere": sink = sinkPolygon; break;
|
||
|
default: return null;
|
||
|
}
|
||
|
d3Geo.geoStream(o, stream(sink));
|
||
|
return sink.result();
|
||
|
}
|
||
|
|
||
|
var points = [],
|
||
|
lines = [];
|
||
|
|
||
|
var sinkPoint = {
|
||
|
point: function(x, y) {
|
||
|
points.push([x, y]);
|
||
|
},
|
||
|
result: function() {
|
||
|
var result = !points.length ? null
|
||
|
: points.length < 2 ? {type: "Point", coordinates: points[0]}
|
||
|
: {type: "MultiPoint", coordinates: points};
|
||
|
points = [];
|
||
|
return result;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var sinkLine = {
|
||
|
lineStart: noop,
|
||
|
point: function(x, y) {
|
||
|
points.push([x, y]);
|
||
|
},
|
||
|
lineEnd: function() {
|
||
|
if (points.length) lines.push(points), points = [];
|
||
|
},
|
||
|
result: function() {
|
||
|
var result = !lines.length ? null
|
||
|
: lines.length < 2 ? {type: "LineString", coordinates: lines[0]}
|
||
|
: {type: "MultiLineString", coordinates: lines};
|
||
|
lines = [];
|
||
|
return result;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var sinkPolygon = {
|
||
|
polygonStart: noop,
|
||
|
lineStart: noop,
|
||
|
point: function(x, y) {
|
||
|
points.push([x, y]);
|
||
|
},
|
||
|
lineEnd: function() {
|
||
|
var n = points.length;
|
||
|
if (n) {
|
||
|
do points.push(points[0].slice()); while (++n < 4);
|
||
|
lines.push(points), points = [];
|
||
|
}
|
||
|
},
|
||
|
polygonEnd: noop,
|
||
|
result: function() {
|
||
|
if (!lines.length) return null;
|
||
|
var polygons = [],
|
||
|
holes = [];
|
||
|
|
||
|
// https://github.com/d3/d3/issues/1558
|
||
|
lines.forEach(function(ring) {
|
||
|
if (clockwise(ring)) polygons.push([ring]);
|
||
|
else holes.push(ring);
|
||
|
});
|
||
|
|
||
|
holes.forEach(function(hole) {
|
||
|
var point = hole[0];
|
||
|
polygons.some(function(polygon) {
|
||
|
if (contains(polygon[0], point)) {
|
||
|
polygon.push(hole);
|
||
|
return true;
|
||
|
}
|
||
|
}) || polygons.push([hole]);
|
||
|
});
|
||
|
|
||
|
lines = [];
|
||
|
|
||
|
return !polygons.length ? null
|
||
|
: polygons.length > 1 ? {type: "MultiPolygon", coordinates: polygons}
|
||
|
: {type: "Polygon", coordinates: polygons[0]};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function quincuncial(project) {
|
||
|
var dx = project(halfPi, 0)[0] - project(-halfPi, 0)[0];
|
||
|
|
||
|
function projectQuincuncial(lambda, phi) {
|
||
|
var t = abs(lambda) < halfPi,
|
||
|
p = project(t ? lambda : lambda > 0 ? lambda - pi : lambda + pi, phi),
|
||
|
x = (p[0] - p[1]) * sqrt1_2,
|
||
|
y = (p[0] + p[1]) * sqrt1_2;
|
||
|
if (t) return [x, y];
|
||
|
var d = dx * sqrt1_2,
|
||
|
s = x > 0 ^ y > 0 ? -1 : 1;
|
||
|
return [s * x - sign(y) * d, s * y - sign(x) * d];
|
||
|
}
|
||
|
|
||
|
if (project.invert) projectQuincuncial.invert = function(x0, y0) {
|
||
|
var x = (x0 + y0) * sqrt1_2,
|
||
|
y = (y0 - x0) * sqrt1_2,
|
||
|
t = abs(x) < 0.5 * dx && abs(y) < 0.5 * dx;
|
||
|
|
||
|
if (!t) {
|
||
|
var d = dx * sqrt1_2,
|
||
|
s = x > 0 ^ y > 0 ? -1 : 1,
|
||
|
x1 = -s * x0 + (y > 0 ? 1 : -1) * d,
|
||
|
y1 = -s * y0 + (x > 0 ? 1 : -1) * d;
|
||
|
x = (-x1 - y1) * sqrt1_2;
|
||
|
y = (x1 - y1) * sqrt1_2;
|
||
|
}
|
||
|
|
||
|
var p = project.invert(x, y);
|
||
|
if (!t) p[0] += x > 0 ? pi : -pi;
|
||
|
return p;
|
||
|
};
|
||
|
|
||
|
return d3Geo.geoProjection(projectQuincuncial)
|
||
|
.rotate([-90, -90, 45])
|
||
|
.clipAngle(180 - 1e-3);
|
||
|
}
|
||
|
|
||
|
function gringorten$1() {
|
||
|
return quincuncial(gringortenRaw)
|
||
|
.scale(176.423);
|
||
|
}
|
||
|
|
||
|
function peirce() {
|
||
|
return quincuncial(guyouRaw)
|
||
|
.scale(111.48);
|
||
|
}
|
||
|
|
||
|
function quantize(input, digits) {
|
||
|
if (!(0 <= (digits = +digits) && digits <= 20)) throw new Error("invalid digits");
|
||
|
|
||
|
function quantizePoint(input) {
|
||
|
var n = input.length, i = 2, output = new Array(n);
|
||
|
output[0] = +input[0].toFixed(digits);
|
||
|
output[1] = +input[1].toFixed(digits);
|
||
|
while (i < n) output[i] = input[i], ++i;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
function quantizePoints(input) {
|
||
|
return input.map(quantizePoint);
|
||
|
}
|
||
|
|
||
|
function quantizePointsNoDuplicates(input) {
|
||
|
var point0 = quantizePoint(input[0]);
|
||
|
var output = [point0];
|
||
|
for (var i = 1; i < input.length; i++) {
|
||
|
var point = quantizePoint(input[i]);
|
||
|
if (point.length > 2 || point[0] != point0[0] || point[1] != point0[1]) {
|
||
|
output.push(point);
|
||
|
point0 = point;
|
||
|
}
|
||
|
}
|
||
|
if (output.length === 1 && input.length > 1) {
|
||
|
output.push(quantizePoint(input[input.length - 1]));
|
||
|
}
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
function quantizePolygon(input) {
|
||
|
return input.map(quantizePointsNoDuplicates);
|
||
|
}
|
||
|
|
||
|
function quantizeGeometry(input) {
|
||
|
if (input == null) return input;
|
||
|
var output;
|
||
|
switch (input.type) {
|
||
|
case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(quantizeGeometry)}; break;
|
||
|
case "Point": output = {type: "Point", coordinates: quantizePoint(input.coordinates)}; break;
|
||
|
case "MultiPoint": output = {type: input.type, coordinates: quantizePoints(input.coordinates)}; break;
|
||
|
case "LineString": output = {type: input.type, coordinates: quantizePointsNoDuplicates(input.coordinates)}; break;
|
||
|
case "MultiLineString": case "Polygon": output = {type: input.type, coordinates: quantizePolygon(input.coordinates)}; break;
|
||
|
case "MultiPolygon": output = {type: "MultiPolygon", coordinates: input.coordinates.map(quantizePolygon)}; break;
|
||
|
default: return input;
|
||
|
}
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
function quantizeFeature(input) {
|
||
|
var output = {type: "Feature", properties: input.properties, geometry: quantizeGeometry(input.geometry)};
|
||
|
if (input.id != null) output.id = input.id;
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
if (input != null) switch (input.type) {
|
||
|
case "Feature": return quantizeFeature(input);
|
||
|
case "FeatureCollection": {
|
||
|
var output = {type: "FeatureCollection", features: input.features.map(quantizeFeature)};
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
return output;
|
||
|
}
|
||
|
default: return quantizeGeometry(input);
|
||
|
}
|
||
|
|
||
|
return input;
|
||
|
}
|
||
|
|
||
|
function rectangularPolyconicRaw(phi0) {
|
||
|
var sinPhi0 = sin(phi0);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var A = sinPhi0 ? tan(lambda * sinPhi0 / 2) / sinPhi0 : lambda / 2;
|
||
|
if (!phi) return [2 * A, -phi0];
|
||
|
var E = 2 * atan(A * sin(phi)),
|
||
|
cotPhi = 1 / tan(phi);
|
||
|
return [
|
||
|
sin(E) * cotPhi,
|
||
|
phi + (1 - cos(E)) * cotPhi - phi0
|
||
|
];
|
||
|
}
|
||
|
|
||
|
// TODO return null for points outside outline.
|
||
|
forward.invert = function(x, y) {
|
||
|
if (abs(y += phi0) < epsilon) return [sinPhi0 ? 2 * atan(sinPhi0 * x / 2) / sinPhi0 : x, 0];
|
||
|
var k = x * x + y * y,
|
||
|
phi = 0,
|
||
|
i = 10, delta;
|
||
|
do {
|
||
|
var tanPhi = tan(phi),
|
||
|
secPhi = 1 / cos(phi),
|
||
|
j = k - 2 * y * phi + phi * phi;
|
||
|
phi -= delta = (tanPhi * j + 2 * (phi - y)) / (2 + j * secPhi * secPhi + 2 * (phi - y) * tanPhi);
|
||
|
} while (abs(delta) > epsilon && --i > 0);
|
||
|
var E = x * (tanPhi = tan(phi)),
|
||
|
A = tan(abs(y) < abs(phi + 1 / tanPhi) ? asin(E) * 0.5 : acos(E) * 0.5 + pi / 4) / sin(phi);
|
||
|
return [
|
||
|
sinPhi0 ? 2 * atan(sinPhi0 * A) / sinPhi0 : 2 * A,
|
||
|
phi
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function rectangularPolyconic() {
|
||
|
return parallel1(rectangularPolyconicRaw)
|
||
|
.scale(131.215);
|
||
|
}
|
||
|
|
||
|
var K$1 = [
|
||
|
[0.9986, -0.062],
|
||
|
[1.0000, 0.0000],
|
||
|
[0.9986, 0.0620],
|
||
|
[0.9954, 0.1240],
|
||
|
[0.9900, 0.1860],
|
||
|
[0.9822, 0.2480],
|
||
|
[0.9730, 0.3100],
|
||
|
[0.9600, 0.3720],
|
||
|
[0.9427, 0.4340],
|
||
|
[0.9216, 0.4958],
|
||
|
[0.8962, 0.5571],
|
||
|
[0.8679, 0.6176],
|
||
|
[0.8350, 0.6769],
|
||
|
[0.7986, 0.7346],
|
||
|
[0.7597, 0.7903],
|
||
|
[0.7186, 0.8435],
|
||
|
[0.6732, 0.8936],
|
||
|
[0.6213, 0.9394],
|
||
|
[0.5722, 0.9761],
|
||
|
[0.5322, 1.0000]
|
||
|
];
|
||
|
|
||
|
K$1.forEach(function(d) {
|
||
|
d[1] *= 1.0144;
|
||
|
});
|
||
|
|
||
|
function robinsonRaw(lambda, phi) {
|
||
|
var i = min(18, abs(phi) * 36 / pi),
|
||
|
i0 = floor(i),
|
||
|
di = i - i0,
|
||
|
ax = (k = K$1[i0])[0],
|
||
|
ay = k[1],
|
||
|
bx = (k = K$1[++i0])[0],
|
||
|
by = k[1],
|
||
|
cx = (k = K$1[min(19, ++i0)])[0],
|
||
|
cy = k[1],
|
||
|
k;
|
||
|
return [
|
||
|
lambda * (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2),
|
||
|
(phi > 0 ? halfPi : -halfPi) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
robinsonRaw.invert = function(x, y) {
|
||
|
var yy = y / halfPi,
|
||
|
phi = yy * 90,
|
||
|
i = min(18, abs(phi / 5)),
|
||
|
i0 = max(0, floor(i));
|
||
|
do {
|
||
|
var ay = K$1[i0][1],
|
||
|
by = K$1[i0 + 1][1],
|
||
|
cy = K$1[min(19, i0 + 2)][1],
|
||
|
u = cy - ay,
|
||
|
v = cy - 2 * by + ay,
|
||
|
t = 2 * (abs(yy) - by) / u,
|
||
|
c = v / u,
|
||
|
di = t * (1 - c * t * (1 - 2 * c * t));
|
||
|
if (di >= 0 || i0 === 1) {
|
||
|
phi = (y >= 0 ? 5 : -5) * (di + i);
|
||
|
var j = 50, delta;
|
||
|
do {
|
||
|
i = min(18, abs(phi) / 5);
|
||
|
i0 = floor(i);
|
||
|
di = i - i0;
|
||
|
ay = K$1[i0][1];
|
||
|
by = K$1[i0 + 1][1];
|
||
|
cy = K$1[min(19, i0 + 2)][1];
|
||
|
phi -= (delta = (y >= 0 ? halfPi : -halfPi) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2) - y) * degrees;
|
||
|
} while (abs(delta) > epsilon2 && --j > 0);
|
||
|
break;
|
||
|
}
|
||
|
} while (--i0 >= 0);
|
||
|
var ax = K$1[i0][0],
|
||
|
bx = K$1[i0 + 1][0],
|
||
|
cx = K$1[min(19, i0 + 2)][0];
|
||
|
return [
|
||
|
x / (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2),
|
||
|
phi * radians
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function robinson() {
|
||
|
return d3Geo.geoProjection(robinsonRaw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function satelliteVerticalRaw(P) {
|
||
|
function forward(lambda, phi) {
|
||
|
var cosPhi = cos(phi),
|
||
|
k = (P - 1) / (P - cosPhi * cos(lambda));
|
||
|
return [
|
||
|
k * cosPhi * sin(lambda),
|
||
|
k * sin(phi)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var rho2 = x * x + y * y,
|
||
|
rho = sqrt(rho2),
|
||
|
sinc = (P - sqrt(1 - rho2 * (P + 1) / (P - 1))) / ((P - 1) / rho + rho / (P - 1));
|
||
|
return [
|
||
|
atan2(x * sinc, rho * sqrt(1 - sinc * sinc)),
|
||
|
rho ? asin(y * sinc / rho) : 0
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function satelliteRaw(P, omega) {
|
||
|
var vertical = satelliteVerticalRaw(P);
|
||
|
if (!omega) return vertical;
|
||
|
var cosOmega = cos(omega),
|
||
|
sinOmega = sin(omega);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var coordinates = vertical(lambda, phi),
|
||
|
y = coordinates[1],
|
||
|
A = y * sinOmega / (P - 1) + cosOmega;
|
||
|
return [
|
||
|
coordinates[0] * cosOmega / A,
|
||
|
y / A
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var k = (P - 1) / (P - 1 - y * sinOmega);
|
||
|
return vertical.invert(k * x, k * y * cosOmega);
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function satellite() {
|
||
|
var distance = 2,
|
||
|
omega = 0,
|
||
|
m = d3Geo.geoProjectionMutator(satelliteRaw),
|
||
|
p = m(distance, omega);
|
||
|
|
||
|
// As a multiple of radius.
|
||
|
p.distance = function(_) {
|
||
|
if (!arguments.length) return distance;
|
||
|
return m(distance = +_, omega);
|
||
|
};
|
||
|
|
||
|
p.tilt = function(_) {
|
||
|
if (!arguments.length) return omega * degrees;
|
||
|
return m(distance, omega = _ * radians);
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.scale(432.147)
|
||
|
.clipAngle(acos(1 / distance) * degrees - 1e-6);
|
||
|
}
|
||
|
|
||
|
var epsilon$1 = 1e-4,
|
||
|
epsilonInverse = 1e4,
|
||
|
x0 = -180, x0e = x0 + epsilon$1,
|
||
|
x1 = 180, x1e = x1 - epsilon$1,
|
||
|
y0 = -90, y0e = y0 + epsilon$1,
|
||
|
y1 = 90, y1e = y1 - epsilon$1;
|
||
|
|
||
|
function nonempty(coordinates) {
|
||
|
return coordinates.length > 0;
|
||
|
}
|
||
|
|
||
|
function quantize$1(x) {
|
||
|
return Math.floor(x * epsilonInverse) / epsilonInverse;
|
||
|
}
|
||
|
|
||
|
function normalizePoint(y) {
|
||
|
return y === y0 || y === y1 ? [0, y] : [x0, quantize$1(y)]; // pole or antimeridian?
|
||
|
}
|
||
|
|
||
|
function clampPoint(p) {
|
||
|
var x = p[0], y = p[1], clamped = false;
|
||
|
if (x <= x0e) x = x0, clamped = true;
|
||
|
else if (x >= x1e) x = x1, clamped = true;
|
||
|
if (y <= y0e) y = y0, clamped = true;
|
||
|
else if (y >= y1e) y = y1, clamped = true;
|
||
|
return clamped ? [x, y] : p;
|
||
|
}
|
||
|
|
||
|
function clampPoints(points) {
|
||
|
return points.map(clampPoint);
|
||
|
}
|
||
|
|
||
|
// For each ring, detect where it crosses the antimeridian or pole.
|
||
|
function extractFragments(rings, polygon, fragments) {
|
||
|
for (var j = 0, m = rings.length; j < m; ++j) {
|
||
|
var ring = rings[j].slice();
|
||
|
|
||
|
// By default, assume that this ring doesn’t need any stitching.
|
||
|
fragments.push({index: -1, polygon: polygon, ring: ring});
|
||
|
|
||
|
for (var i = 0, n = ring.length; i < n; ++i) {
|
||
|
var point = ring[i],
|
||
|
x = point[0],
|
||
|
y = point[1];
|
||
|
|
||
|
// If this is an antimeridian or polar point…
|
||
|
if (x <= x0e || x >= x1e || y <= y0e || y >= y1e) {
|
||
|
ring[i] = clampPoint(point);
|
||
|
|
||
|
// Advance through any antimeridian or polar points…
|
||
|
for (var k = i + 1; k < n; ++k) {
|
||
|
var pointk = ring[k],
|
||
|
xk = pointk[0],
|
||
|
yk = pointk[1];
|
||
|
if (xk > x0e && xk < x1e && yk > y0e && yk < y1e) break;
|
||
|
}
|
||
|
|
||
|
// If this was just a single antimeridian or polar point,
|
||
|
// we don’t need to cut this ring into a fragment;
|
||
|
// we can just leave it as-is.
|
||
|
if (k === i + 1) continue;
|
||
|
|
||
|
// Otherwise, if this is not the first point in the ring,
|
||
|
// cut the current fragment so that it ends at the current point.
|
||
|
// The current point is also normalized for later joining.
|
||
|
if (i) {
|
||
|
var fragmentBefore = {index: -1, polygon: polygon, ring: ring.slice(0, i + 1)};
|
||
|
fragmentBefore.ring[fragmentBefore.ring.length - 1] = normalizePoint(y);
|
||
|
fragments[fragments.length - 1] = fragmentBefore;
|
||
|
}
|
||
|
|
||
|
// If the ring started with an antimeridian fragment,
|
||
|
// we can ignore that fragment entirely.
|
||
|
else fragments.pop();
|
||
|
|
||
|
// If the remainder of the ring is an antimeridian fragment,
|
||
|
// move on to the next ring.
|
||
|
if (k >= n) break;
|
||
|
|
||
|
// Otherwise, add the remaining ring fragment and continue.
|
||
|
fragments.push({index: -1, polygon: polygon, ring: ring = ring.slice(k - 1)});
|
||
|
ring[0] = normalizePoint(ring[0][1]);
|
||
|
i = -1;
|
||
|
n = ring.length;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now stitch the fragments back together into rings.
|
||
|
function stitchFragments(fragments) {
|
||
|
var i, n = fragments.length;
|
||
|
|
||
|
// To connect the fragments start-to-end, create a simple index by end.
|
||
|
var fragmentByStart = {},
|
||
|
fragmentByEnd = {},
|
||
|
fragment,
|
||
|
start,
|
||
|
startFragment,
|
||
|
end,
|
||
|
endFragment;
|
||
|
|
||
|
// For each fragment…
|
||
|
for (i = 0; i < n; ++i) {
|
||
|
fragment = fragments[i];
|
||
|
start = fragment.ring[0];
|
||
|
end = fragment.ring[fragment.ring.length - 1];
|
||
|
|
||
|
// If this fragment is closed, add it as a standalone ring.
|
||
|
if (start[0] === end[0] && start[1] === end[1]) {
|
||
|
fragment.polygon.push(fragment.ring);
|
||
|
fragments[i] = null;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fragment.index = i;
|
||
|
fragmentByStart[start] = fragmentByEnd[end] = fragment;
|
||
|
}
|
||
|
|
||
|
// For each open fragment…
|
||
|
for (i = 0; i < n; ++i) {
|
||
|
fragment = fragments[i];
|
||
|
if (fragment) {
|
||
|
start = fragment.ring[0];
|
||
|
end = fragment.ring[fragment.ring.length - 1];
|
||
|
startFragment = fragmentByEnd[start];
|
||
|
endFragment = fragmentByStart[end];
|
||
|
|
||
|
delete fragmentByStart[start];
|
||
|
delete fragmentByEnd[end];
|
||
|
|
||
|
// If this fragment is closed, add it as a standalone ring.
|
||
|
if (start[0] === end[0] && start[1] === end[1]) {
|
||
|
fragment.polygon.push(fragment.ring);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (startFragment) {
|
||
|
delete fragmentByEnd[start];
|
||
|
delete fragmentByStart[startFragment.ring[0]];
|
||
|
startFragment.ring.pop(); // drop the shared coordinate
|
||
|
fragments[startFragment.index] = null;
|
||
|
fragment = {index: -1, polygon: startFragment.polygon, ring: startFragment.ring.concat(fragment.ring)};
|
||
|
|
||
|
if (startFragment === endFragment) {
|
||
|
// Connect both ends to this single fragment to create a ring.
|
||
|
fragment.polygon.push(fragment.ring);
|
||
|
} else {
|
||
|
fragment.index = n++;
|
||
|
fragments.push(fragmentByStart[fragment.ring[0]] = fragmentByEnd[fragment.ring[fragment.ring.length - 1]] = fragment);
|
||
|
}
|
||
|
} else if (endFragment) {
|
||
|
delete fragmentByStart[end];
|
||
|
delete fragmentByEnd[endFragment.ring[endFragment.ring.length - 1]];
|
||
|
fragment.ring.pop(); // drop the shared coordinate
|
||
|
fragment = {index: n++, polygon: endFragment.polygon, ring: fragment.ring.concat(endFragment.ring)};
|
||
|
fragments[endFragment.index] = null;
|
||
|
fragments.push(fragmentByStart[fragment.ring[0]] = fragmentByEnd[fragment.ring[fragment.ring.length - 1]] = fragment);
|
||
|
} else {
|
||
|
fragment.ring.push(fragment.ring[0]); // close ring
|
||
|
fragment.polygon.push(fragment.ring);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function stitchFeature(input) {
|
||
|
var output = {type: "Feature", geometry: stitchGeometry(input.geometry)};
|
||
|
if (input.id != null) output.id = input.id;
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
if (input.properties != null) output.properties = input.properties;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
function stitchGeometry(input) {
|
||
|
if (input == null) return input;
|
||
|
var output, fragments, i, n;
|
||
|
switch (input.type) {
|
||
|
case "GeometryCollection": output = {type: "GeometryCollection", geometries: input.geometries.map(stitchGeometry)}; break;
|
||
|
case "Point": output = {type: "Point", coordinates: clampPoint(input.coordinates)}; break;
|
||
|
case "MultiPoint": case "LineString": output = {type: input.type, coordinates: clampPoints(input.coordinates)}; break;
|
||
|
case "MultiLineString": output = {type: "MultiLineString", coordinates: input.coordinates.map(clampPoints)}; break;
|
||
|
case "Polygon": {
|
||
|
var polygon = [];
|
||
|
extractFragments(input.coordinates, polygon, fragments = []);
|
||
|
stitchFragments(fragments);
|
||
|
output = {type: "Polygon", coordinates: polygon};
|
||
|
break;
|
||
|
}
|
||
|
case "MultiPolygon": {
|
||
|
fragments = [], i = -1, n = input.coordinates.length;
|
||
|
var polygons = new Array(n);
|
||
|
while (++i < n) extractFragments(input.coordinates[i], polygons[i] = [], fragments);
|
||
|
stitchFragments(fragments);
|
||
|
output = {type: "MultiPolygon", coordinates: polygons.filter(nonempty)};
|
||
|
break;
|
||
|
}
|
||
|
default: return input;
|
||
|
}
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
function stitch(input) {
|
||
|
if (input == null) return input;
|
||
|
switch (input.type) {
|
||
|
case "Feature": return stitchFeature(input);
|
||
|
case "FeatureCollection": {
|
||
|
var output = {type: "FeatureCollection", features: input.features.map(stitchFeature)};
|
||
|
if (input.bbox != null) output.bbox = input.bbox;
|
||
|
return output;
|
||
|
}
|
||
|
default: return stitchGeometry(input);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function timesRaw(lambda, phi) {
|
||
|
var t = tan(phi / 2),
|
||
|
s = sin(quarterPi * t);
|
||
|
return [
|
||
|
lambda * (0.74482 - 0.34588 * s * s),
|
||
|
1.70711 * t
|
||
|
];
|
||
|
}
|
||
|
|
||
|
timesRaw.invert = function(x, y) {
|
||
|
var t = y / 1.70711,
|
||
|
s = sin(quarterPi * t);
|
||
|
return [
|
||
|
x / (0.74482 - 0.34588 * s * s),
|
||
|
2 * atan(t)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function times() {
|
||
|
return d3Geo.geoProjection(timesRaw)
|
||
|
.scale(146.153);
|
||
|
}
|
||
|
|
||
|
// Compute the origin as the midpoint of the two reference points.
|
||
|
// Rotate one of the reference points by the origin.
|
||
|
// Apply the spherical law of sines to compute gamma rotation.
|
||
|
function twoPoint(raw, p0, p1) {
|
||
|
var i = d3Geo.geoInterpolate(p0, p1),
|
||
|
o = i(0.5),
|
||
|
a = d3Geo.geoRotation([-o[0], -o[1]])(p0),
|
||
|
b = i.distance / 2,
|
||
|
y = -asin(sin(a[1] * radians) / sin(b)),
|
||
|
R = [-o[0], -o[1], -(a[0] > 0 ? pi - y : y) * degrees],
|
||
|
p = d3Geo.geoProjection(raw(b)).rotate(R),
|
||
|
r = d3Geo.geoRotation(R),
|
||
|
center = p.center;
|
||
|
|
||
|
delete p.rotate;
|
||
|
|
||
|
p.center = function(_) {
|
||
|
return arguments.length ? center(r(_)) : r.invert(center());
|
||
|
};
|
||
|
|
||
|
return p
|
||
|
.clipAngle(90);
|
||
|
}
|
||
|
|
||
|
function twoPointAzimuthalRaw(d) {
|
||
|
var cosd = cos(d);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var coordinates = d3Geo.geoGnomonicRaw(lambda, phi);
|
||
|
coordinates[0] *= cosd;
|
||
|
return coordinates;
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
return d3Geo.geoGnomonicRaw.invert(x / cosd, y);
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function twoPointAzimuthalUsa() {
|
||
|
return twoPointAzimuthal([-158, 21.5], [-77, 39])
|
||
|
.clipAngle(60)
|
||
|
.scale(400);
|
||
|
}
|
||
|
|
||
|
function twoPointAzimuthal(p0, p1) {
|
||
|
return twoPoint(twoPointAzimuthalRaw, p0, p1);
|
||
|
}
|
||
|
|
||
|
// TODO clip to ellipse
|
||
|
function twoPointEquidistantRaw(z0) {
|
||
|
if (!(z0 *= 2)) return d3Geo.geoAzimuthalEquidistantRaw;
|
||
|
var lambdaa = -z0 / 2,
|
||
|
lambdab = -lambdaa,
|
||
|
z02 = z0 * z0,
|
||
|
tanLambda0 = tan(lambdab),
|
||
|
S = 0.5 / sin(lambdab);
|
||
|
|
||
|
function forward(lambda, phi) {
|
||
|
var za = acos(cos(phi) * cos(lambda - lambdaa)),
|
||
|
zb = acos(cos(phi) * cos(lambda - lambdab)),
|
||
|
ys = phi < 0 ? -1 : 1;
|
||
|
za *= za, zb *= zb;
|
||
|
return [
|
||
|
(za - zb) / (2 * z0),
|
||
|
ys * sqrt(4 * z02 * zb - (z02 - za + zb) * (z02 - za + zb)) / (2 * z0)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var y2 = y * y,
|
||
|
cosza = cos(sqrt(y2 + (t = x + lambdaa) * t)),
|
||
|
coszb = cos(sqrt(y2 + (t = x + lambdab) * t)),
|
||
|
t,
|
||
|
d;
|
||
|
return [
|
||
|
atan2(d = cosza - coszb, t = (cosza + coszb) * tanLambda0),
|
||
|
(y < 0 ? -1 : 1) * acos(sqrt(t * t + d * d) * S)
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function twoPointEquidistantUsa() {
|
||
|
return twoPointEquidistant([-158, 21.5], [-77, 39])
|
||
|
.clipAngle(130)
|
||
|
.scale(122.571);
|
||
|
}
|
||
|
|
||
|
function twoPointEquidistant(p0, p1) {
|
||
|
return twoPoint(twoPointEquidistantRaw, p0, p1);
|
||
|
}
|
||
|
|
||
|
function vanDerGrintenRaw(lambda, phi) {
|
||
|
if (abs(phi) < epsilon) return [lambda, 0];
|
||
|
var sinTheta = abs(phi / halfPi),
|
||
|
theta = asin(sinTheta);
|
||
|
if (abs(lambda) < epsilon || abs(abs(phi) - halfPi) < epsilon) return [0, sign(phi) * pi * tan(theta / 2)];
|
||
|
var cosTheta = cos(theta),
|
||
|
A = abs(pi / lambda - lambda / pi) / 2,
|
||
|
A2 = A * A,
|
||
|
G = cosTheta / (sinTheta + cosTheta - 1),
|
||
|
P = G * (2 / sinTheta - 1),
|
||
|
P2 = P * P,
|
||
|
P2_A2 = P2 + A2,
|
||
|
G_P2 = G - P2,
|
||
|
Q = A2 + G;
|
||
|
return [
|
||
|
sign(lambda) * pi * (A * G_P2 + sqrt(A2 * G_P2 * G_P2 - P2_A2 * (G * G - P2))) / P2_A2,
|
||
|
sign(phi) * pi * (P * Q - A * sqrt((A2 + 1) * P2_A2 - Q * Q)) / P2_A2
|
||
|
];
|
||
|
}
|
||
|
|
||
|
vanDerGrintenRaw.invert = function(x, y) {
|
||
|
if (abs(y) < epsilon) return [x, 0];
|
||
|
if (abs(x) < epsilon) return [0, halfPi * sin(2 * atan(y / pi))];
|
||
|
var x2 = (x /= pi) * x,
|
||
|
y2 = (y /= pi) * y,
|
||
|
x2_y2 = x2 + y2,
|
||
|
z = x2_y2 * x2_y2,
|
||
|
c1 = -abs(y) * (1 + x2_y2),
|
||
|
c2 = c1 - 2 * y2 + x2,
|
||
|
c3 = -2 * c1 + 1 + 2 * y2 + z,
|
||
|
d = y2 / c3 + (2 * c2 * c2 * c2 / (c3 * c3 * c3) - 9 * c1 * c2 / (c3 * c3)) / 27,
|
||
|
a1 = (c1 - c2 * c2 / (3 * c3)) / c3,
|
||
|
m1 = 2 * sqrt(-a1 / 3),
|
||
|
theta1 = acos(3 * d / (a1 * m1)) / 3;
|
||
|
return [
|
||
|
pi * (x2_y2 - 1 + sqrt(1 + 2 * (x2 - y2) + z)) / (2 * x),
|
||
|
sign(y) * pi * (-m1 * cos(theta1 + pi / 3) - c2 / (3 * c3))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function vanDerGrinten() {
|
||
|
return d3Geo.geoProjection(vanDerGrintenRaw)
|
||
|
.scale(79.4183);
|
||
|
}
|
||
|
|
||
|
function vanDerGrinten2Raw(lambda, phi) {
|
||
|
if (abs(phi) < epsilon) return [lambda, 0];
|
||
|
var sinTheta = abs(phi / halfPi),
|
||
|
theta = asin(sinTheta);
|
||
|
if (abs(lambda) < epsilon || abs(abs(phi) - halfPi) < epsilon) return [0, sign(phi) * pi * tan(theta / 2)];
|
||
|
var cosTheta = cos(theta),
|
||
|
A = abs(pi / lambda - lambda / pi) / 2,
|
||
|
A2 = A * A,
|
||
|
x1 = cosTheta * (sqrt(1 + A2) - A * cosTheta) / (1 + A2 * sinTheta * sinTheta);
|
||
|
return [
|
||
|
sign(lambda) * pi * x1,
|
||
|
sign(phi) * pi * sqrt(1 - x1 * (2 * A + x1))
|
||
|
];
|
||
|
}
|
||
|
|
||
|
vanDerGrinten2Raw.invert = function(x, y) {
|
||
|
if (!x) return [0, halfPi * sin(2 * atan(y / pi))];
|
||
|
var x1 = abs(x / pi),
|
||
|
A = (1 - x1 * x1 - (y /= pi) * y) / (2 * x1),
|
||
|
A2 = A * A,
|
||
|
B = sqrt(A2 + 1);
|
||
|
return [
|
||
|
sign(x) * pi * (B - A),
|
||
|
sign(y) * halfPi * sin(2 * atan2(sqrt((1 - 2 * A * x1) * (A + B) - x1), sqrt(B + A + x1)))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function vanDerGrinten2() {
|
||
|
return d3Geo.geoProjection(vanDerGrinten2Raw)
|
||
|
.scale(79.4183);
|
||
|
}
|
||
|
|
||
|
function vanDerGrinten3Raw(lambda, phi) {
|
||
|
if (abs(phi) < epsilon) return [lambda, 0];
|
||
|
var sinTheta = phi / halfPi,
|
||
|
theta = asin(sinTheta);
|
||
|
if (abs(lambda) < epsilon || abs(abs(phi) - halfPi) < epsilon) return [0, pi * tan(theta / 2)];
|
||
|
var A = (pi / lambda - lambda / pi) / 2,
|
||
|
y1 = sinTheta / (1 + cos(theta));
|
||
|
return [
|
||
|
pi * (sign(lambda) * sqrt(A * A + 1 - y1 * y1) - A),
|
||
|
pi * y1
|
||
|
];
|
||
|
}
|
||
|
|
||
|
vanDerGrinten3Raw.invert = function(x, y) {
|
||
|
if (!y) return [x, 0];
|
||
|
var y1 = y / pi,
|
||
|
A = (pi * pi * (1 - y1 * y1) - x * x) / (2 * pi * x);
|
||
|
return [
|
||
|
x ? pi * (sign(x) * sqrt(A * A + 1) - A) : 0,
|
||
|
halfPi * sin(2 * atan(y1))
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function vanDerGrinten3() {
|
||
|
return d3Geo.geoProjection(vanDerGrinten3Raw)
|
||
|
.scale(79.4183);
|
||
|
}
|
||
|
|
||
|
function vanDerGrinten4Raw(lambda, phi) {
|
||
|
if (!phi) return [lambda, 0];
|
||
|
var phi0 = abs(phi);
|
||
|
if (!lambda || phi0 === halfPi) return [0, phi];
|
||
|
var B = phi0 / halfPi,
|
||
|
B2 = B * B,
|
||
|
C = (8 * B - B2 * (B2 + 2) - 5) / (2 * B2 * (B - 1)),
|
||
|
C2 = C * C,
|
||
|
BC = B * C,
|
||
|
B_C2 = B2 + C2 + 2 * BC,
|
||
|
B_3C = B + 3 * C,
|
||
|
lambda0 = lambda / halfPi,
|
||
|
lambda1 = lambda0 + 1 / lambda0,
|
||
|
D = sign(abs(lambda) - halfPi) * sqrt(lambda1 * lambda1 - 4),
|
||
|
D2 = D * D,
|
||
|
F = B_C2 * (B2 + C2 * D2 - 1) + (1 - B2) * (B2 * (B_3C * B_3C + 4 * C2) + 12 * BC * C2 + 4 * C2 * C2),
|
||
|
x1 = (D * (B_C2 + C2 - 1) + 2 * sqrt(F)) / (4 * B_C2 + D2);
|
||
|
return [
|
||
|
sign(lambda) * halfPi * x1,
|
||
|
sign(phi) * halfPi * sqrt(1 + D * abs(x1) - x1 * x1)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
vanDerGrinten4Raw.invert = function(x, y) {
|
||
|
var delta;
|
||
|
if (!x || !y) return [x, y];
|
||
|
y /= pi;
|
||
|
var x1 = sign(x) * x / halfPi,
|
||
|
D = (x1 * x1 - 1 + 4 * y * y) / abs(x1),
|
||
|
D2 = D * D,
|
||
|
B = 2 * y,
|
||
|
i = 50;
|
||
|
do {
|
||
|
var B2 = B * B,
|
||
|
C = (8 * B - B2 * (B2 + 2) - 5) / (2 * B2 * (B - 1)),
|
||
|
C_ = (3 * B - B2 * B - 10) / (2 * B2 * B),
|
||
|
C2 = C * C,
|
||
|
BC = B * C,
|
||
|
B_C = B + C,
|
||
|
B_C2 = B_C * B_C,
|
||
|
B_3C = B + 3 * C,
|
||
|
F = B_C2 * (B2 + C2 * D2 - 1) + (1 - B2) * (B2 * (B_3C * B_3C + 4 * C2) + C2 * (12 * BC + 4 * C2)),
|
||
|
F_ = -2 * B_C * (4 * BC * C2 + (1 - 4 * B2 + 3 * B2 * B2) * (1 + C_) + C2 * (-6 + 14 * B2 - D2 + (-8 + 8 * B2 - 2 * D2) * C_) + BC * (-8 + 12 * B2 + (-10 + 10 * B2 - D2) * C_)),
|
||
|
sqrtF = sqrt(F),
|
||
|
f = D * (B_C2 + C2 - 1) + 2 * sqrtF - x1 * (4 * B_C2 + D2),
|
||
|
f_ = D * (2 * C * C_ + 2 * B_C * (1 + C_)) + F_ / sqrtF - 8 * B_C * (D * (-1 + C2 + B_C2) + 2 * sqrtF) * (1 + C_) / (D2 + 4 * B_C2);
|
||
|
B -= delta = f / f_;
|
||
|
} while (delta > epsilon && --i > 0);
|
||
|
return [
|
||
|
sign(x) * (sqrt(D * D + 4) + D) * pi / 4,
|
||
|
halfPi * B
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function vanDerGrinten4() {
|
||
|
return d3Geo.geoProjection(vanDerGrinten4Raw)
|
||
|
.scale(127.16);
|
||
|
}
|
||
|
|
||
|
function wagnerFormula(cx, cy, m1, m2, n) {
|
||
|
function forward(lambda, phi) {
|
||
|
var s = m1 * sin(m2 * phi),
|
||
|
c0 = sqrt(1 - s * s),
|
||
|
c1 = sqrt(2 / (1 + c0 * cos(lambda *= n)));
|
||
|
return [
|
||
|
cx * c0 * c1 * sin(lambda),
|
||
|
cy * s * c1
|
||
|
];
|
||
|
}
|
||
|
|
||
|
forward.invert = function(x, y) {
|
||
|
var t1 = x / cx,
|
||
|
t2 = y / cy,
|
||
|
p = sqrt(t1 * t1 + t2 * t2),
|
||
|
c = 2 * asin(p / 2);
|
||
|
return [
|
||
|
atan2(x * tan(c), cx * p) / n,
|
||
|
p && asin(y * sin(c) / (cy * m1 * p)) / m2
|
||
|
];
|
||
|
};
|
||
|
|
||
|
return forward;
|
||
|
}
|
||
|
|
||
|
function wagnerRaw(poleline, parallels, inflation, ratio) {
|
||
|
// 60 is always used as reference parallel
|
||
|
var phi1 = pi / 3;
|
||
|
|
||
|
// sanitizing the input values
|
||
|
// poleline and parallels may approximate but never equal 0
|
||
|
poleline = max(poleline, epsilon);
|
||
|
parallels = max(parallels, epsilon);
|
||
|
// poleline must be <= 90; parallels may approximate but never equal 180
|
||
|
poleline = min(poleline, halfPi);
|
||
|
parallels = min(parallels, pi - epsilon);
|
||
|
// 0 <= inflation <= 99.999
|
||
|
inflation = max(inflation, 0);
|
||
|
inflation = min(inflation, 100 - epsilon);
|
||
|
// ratio > 0.
|
||
|
// sensible values, i.e. something that renders a map which still can be
|
||
|
// recognized as world map, are e.g. 20 <= ratio <= 1000.
|
||
|
ratio = max(ratio, epsilon);
|
||
|
|
||
|
// convert values from boehm notation
|
||
|
// areal inflation e.g. from 0 to 1 or 20 to 1.2:
|
||
|
var vinflation = inflation/100 + 1;
|
||
|
// axial ratio e.g. from 200 to 2:
|
||
|
var vratio = ratio / 100;
|
||
|
// the other ones are a bit more complicated...
|
||
|
var m2 = acos(vinflation * cos(phi1)) / phi1,
|
||
|
m1 = sin(poleline) / sin(m2 * halfPi),
|
||
|
n = parallels / pi,
|
||
|
k = sqrt(vratio * sin(poleline / 2) / sin(parallels / 2)),
|
||
|
cx = k / sqrt(n * m1 * m2),
|
||
|
cy = 1 / (k * sqrt(n * m1 * m2));
|
||
|
|
||
|
return wagnerFormula(cx, cy, m1, m2, n);
|
||
|
}
|
||
|
|
||
|
function wagner() {
|
||
|
// default values generate wagner8
|
||
|
var poleline = 65 * radians,
|
||
|
parallels = 60 * radians,
|
||
|
inflation = 20,
|
||
|
ratio = 200,
|
||
|
mutate = d3Geo.geoProjectionMutator(wagnerRaw),
|
||
|
projection = mutate(poleline, parallels, inflation, ratio);
|
||
|
|
||
|
projection.poleline = function(_) {
|
||
|
return arguments.length ? mutate(poleline = +_ * radians, parallels, inflation, ratio) : poleline * degrees;
|
||
|
};
|
||
|
|
||
|
projection.parallels = function(_) {
|
||
|
return arguments.length ? mutate(poleline, parallels = +_ * radians, inflation, ratio) : parallels * degrees;
|
||
|
};
|
||
|
projection.inflation = function(_) {
|
||
|
return arguments.length ? mutate(poleline, parallels, inflation = +_, ratio) : inflation;
|
||
|
};
|
||
|
projection.ratio = function(_) {
|
||
|
return arguments.length ? mutate(poleline, parallels, inflation, ratio = +_) : ratio;
|
||
|
};
|
||
|
|
||
|
return projection
|
||
|
.scale(163.775);
|
||
|
}
|
||
|
|
||
|
function wagner7() {
|
||
|
return wagner()
|
||
|
.poleline(65)
|
||
|
.parallels(60)
|
||
|
.inflation(0)
|
||
|
.ratio(200)
|
||
|
.scale(172.633);
|
||
|
}
|
||
|
|
||
|
var A = 4 * pi + 3 * sqrt(3),
|
||
|
B = 2 * sqrt(2 * pi * sqrt(3) / A);
|
||
|
|
||
|
var wagner4Raw = mollweideBromleyRaw(B * sqrt(3) / pi, B, A / 6);
|
||
|
|
||
|
function wagner4() {
|
||
|
return d3Geo.geoProjection(wagner4Raw)
|
||
|
.scale(176.84);
|
||
|
}
|
||
|
|
||
|
function wagner6Raw(lambda, phi) {
|
||
|
return [lambda * sqrt(1 - 3 * phi * phi / (pi * pi)), phi];
|
||
|
}
|
||
|
|
||
|
wagner6Raw.invert = function(x, y) {
|
||
|
return [x / sqrt(1 - 3 * y * y / (pi * pi)), y];
|
||
|
};
|
||
|
|
||
|
function wagner6() {
|
||
|
return d3Geo.geoProjection(wagner6Raw)
|
||
|
.scale(152.63);
|
||
|
}
|
||
|
|
||
|
function wiechelRaw(lambda, phi) {
|
||
|
var cosPhi = cos(phi),
|
||
|
sinPhi = cos(lambda) * cosPhi,
|
||
|
sin1_Phi = 1 - sinPhi,
|
||
|
cosLambda = cos(lambda = atan2(sin(lambda) * cosPhi, -sin(phi))),
|
||
|
sinLambda = sin(lambda);
|
||
|
cosPhi = sqrt(1 - sinPhi * sinPhi);
|
||
|
return [
|
||
|
sinLambda * cosPhi - cosLambda * sin1_Phi,
|
||
|
-cosLambda * cosPhi - sinLambda * sin1_Phi
|
||
|
];
|
||
|
}
|
||
|
|
||
|
wiechelRaw.invert = function(x, y) {
|
||
|
var w = (x * x + y * y) / -2,
|
||
|
k = sqrt(-w * (2 + w)),
|
||
|
b = y * w + x * k,
|
||
|
a = x * w - y * k,
|
||
|
D = sqrt(a * a + b * b);
|
||
|
return [
|
||
|
atan2(k * b, D * (1 + w)),
|
||
|
D ? -asin(k * a / D) : 0
|
||
|
];
|
||
|
};
|
||
|
|
||
|
function wiechel() {
|
||
|
return d3Geo.geoProjection(wiechelRaw)
|
||
|
.rotate([0, -90, 45])
|
||
|
.scale(124.75)
|
||
|
.clipAngle(180 - 1e-3);
|
||
|
}
|
||
|
|
||
|
function winkel3Raw(lambda, phi) {
|
||
|
var coordinates = aitoffRaw(lambda, phi);
|
||
|
return [
|
||
|
(coordinates[0] + lambda / halfPi) / 2,
|
||
|
(coordinates[1] + phi) / 2
|
||
|
];
|
||
|
}
|
||
|
|
||
|
winkel3Raw.invert = function(x, y) {
|
||
|
var lambda = x, phi = y, i = 25;
|
||
|
do {
|
||
|
var cosphi = cos(phi),
|
||
|
sinphi = sin(phi),
|
||
|
sin_2phi = sin(2 * phi),
|
||
|
sin2phi = sinphi * sinphi,
|
||
|
cos2phi = cosphi * cosphi,
|
||
|
sinlambda = sin(lambda),
|
||
|
coslambda_2 = cos(lambda / 2),
|
||
|
sinlambda_2 = sin(lambda / 2),
|
||
|
sin2lambda_2 = sinlambda_2 * sinlambda_2,
|
||
|
C = 1 - cos2phi * coslambda_2 * coslambda_2,
|
||
|
E = C ? acos(cosphi * coslambda_2) * sqrt(F = 1 / C) : F = 0,
|
||
|
F,
|
||
|
fx = 0.5 * (2 * E * cosphi * sinlambda_2 + lambda / halfPi) - x,
|
||
|
fy = 0.5 * (E * sinphi + phi) - y,
|
||
|
dxdlambda = 0.5 * F * (cos2phi * sin2lambda_2 + E * cosphi * coslambda_2 * sin2phi) + 0.5 / halfPi,
|
||
|
dxdphi = F * (sinlambda * sin_2phi / 4 - E * sinphi * sinlambda_2),
|
||
|
dydlambda = 0.125 * F * (sin_2phi * sinlambda_2 - E * sinphi * cos2phi * sinlambda),
|
||
|
dydphi = 0.5 * F * (sin2phi * coslambda_2 + E * sin2lambda_2 * cosphi) + 0.5,
|
||
|
denominator = dxdphi * dydlambda - dydphi * dxdlambda,
|
||
|
dlambda = (fy * dxdphi - fx * dydphi) / denominator,
|
||
|
dphi = (fx * dydlambda - fy * dxdlambda) / denominator;
|
||
|
lambda -= dlambda, phi -= dphi;
|
||
|
} while ((abs(dlambda) > epsilon || abs(dphi) > epsilon) && --i > 0);
|
||
|
return [lambda, phi];
|
||
|
};
|
||
|
|
||
|
function winkel3() {
|
||
|
return d3Geo.geoProjection(winkel3Raw)
|
||
|
.scale(158.837);
|
||
|
}
|
||
|
|
||
|
exports.geoNaturalEarth = d3Geo.geoNaturalEarth1;
|
||
|
exports.geoNaturalEarthRaw = d3Geo.geoNaturalEarth1Raw;
|
||
|
exports.geoAiry = airy;
|
||
|
exports.geoAiryRaw = airyRaw;
|
||
|
exports.geoAitoff = aitoff;
|
||
|
exports.geoAitoffRaw = aitoffRaw;
|
||
|
exports.geoArmadillo = armadillo;
|
||
|
exports.geoArmadilloRaw = armadilloRaw;
|
||
|
exports.geoAugust = august;
|
||
|
exports.geoAugustRaw = augustRaw;
|
||
|
exports.geoBaker = baker;
|
||
|
exports.geoBakerRaw = bakerRaw;
|
||
|
exports.geoBerghaus = berghaus;
|
||
|
exports.geoBerghausRaw = berghausRaw;
|
||
|
exports.geoBertin1953 = bertin;
|
||
|
exports.geoBertin1953Raw = bertin1953Raw;
|
||
|
exports.geoBoggs = boggs;
|
||
|
exports.geoBoggsRaw = boggsRaw;
|
||
|
exports.geoBonne = bonne;
|
||
|
exports.geoBonneRaw = bonneRaw;
|
||
|
exports.geoBottomley = bottomley;
|
||
|
exports.geoBottomleyRaw = bottomleyRaw;
|
||
|
exports.geoBromley = bromley;
|
||
|
exports.geoBromleyRaw = bromleyRaw;
|
||
|
exports.geoChamberlin = chamberlin;
|
||
|
exports.geoChamberlinRaw = chamberlinRaw;
|
||
|
exports.geoChamberlinAfrica = chamberlinAfrica;
|
||
|
exports.geoCollignon = collignon;
|
||
|
exports.geoCollignonRaw = collignonRaw;
|
||
|
exports.geoCraig = craig;
|
||
|
exports.geoCraigRaw = craigRaw;
|
||
|
exports.geoCraster = craster;
|
||
|
exports.geoCrasterRaw = crasterRaw;
|
||
|
exports.geoCylindricalEqualArea = cylindricalEqualArea;
|
||
|
exports.geoCylindricalEqualAreaRaw = cylindricalEqualAreaRaw;
|
||
|
exports.geoCylindricalStereographic = cylindricalStereographic;
|
||
|
exports.geoCylindricalStereographicRaw = cylindricalStereographicRaw;
|
||
|
exports.geoEckert1 = eckert1;
|
||
|
exports.geoEckert1Raw = eckert1Raw;
|
||
|
exports.geoEckert2 = eckert2;
|
||
|
exports.geoEckert2Raw = eckert2Raw;
|
||
|
exports.geoEckert3 = eckert3;
|
||
|
exports.geoEckert3Raw = eckert3Raw;
|
||
|
exports.geoEckert4 = eckert4;
|
||
|
exports.geoEckert4Raw = eckert4Raw;
|
||
|
exports.geoEckert5 = eckert5;
|
||
|
exports.geoEckert5Raw = eckert5Raw;
|
||
|
exports.geoEckert6 = eckert6;
|
||
|
exports.geoEckert6Raw = eckert6Raw;
|
||
|
exports.geoEisenlohr = eisenlohr;
|
||
|
exports.geoEisenlohrRaw = eisenlohrRaw;
|
||
|
exports.geoFahey = fahey;
|
||
|
exports.geoFaheyRaw = faheyRaw;
|
||
|
exports.geoFoucaut = foucaut;
|
||
|
exports.geoFoucautRaw = foucautRaw;
|
||
|
exports.geoFoucautSinusoidal = foucautSinusoidal;
|
||
|
exports.geoFoucautSinusoidalRaw = foucautSinusoidalRaw;
|
||
|
exports.geoGilbert = gilbert;
|
||
|
exports.geoGingery = gingery;
|
||
|
exports.geoGingeryRaw = gingeryRaw;
|
||
|
exports.geoGinzburg4 = ginzburg4;
|
||
|
exports.geoGinzburg4Raw = ginzburg4Raw;
|
||
|
exports.geoGinzburg5 = ginzburg5;
|
||
|
exports.geoGinzburg5Raw = ginzburg5Raw;
|
||
|
exports.geoGinzburg6 = ginzburg6;
|
||
|
exports.geoGinzburg6Raw = ginzburg6Raw;
|
||
|
exports.geoGinzburg8 = ginzburg8;
|
||
|
exports.geoGinzburg8Raw = ginzburg8Raw;
|
||
|
exports.geoGinzburg9 = ginzburg9;
|
||
|
exports.geoGinzburg9Raw = ginzburg9Raw;
|
||
|
exports.geoGringorten = gringorten;
|
||
|
exports.geoGringortenRaw = gringortenRaw;
|
||
|
exports.geoGuyou = guyou;
|
||
|
exports.geoGuyouRaw = guyouRaw;
|
||
|
exports.geoHammer = hammer;
|
||
|
exports.geoHammerRaw = hammerRaw;
|
||
|
exports.geoHammerRetroazimuthal = hammerRetroazimuthal;
|
||
|
exports.geoHammerRetroazimuthalRaw = hammerRetroazimuthalRaw;
|
||
|
exports.geoHealpix = healpix;
|
||
|
exports.geoHealpixRaw = healpixRaw;
|
||
|
exports.geoHill = hill;
|
||
|
exports.geoHillRaw = hillRaw;
|
||
|
exports.geoHomolosine = homolosine;
|
||
|
exports.geoHomolosineRaw = homolosineRaw;
|
||
|
exports.geoHufnagel = hufnagel;
|
||
|
exports.geoHufnagelRaw = hufnagelRaw;
|
||
|
exports.geoHyperelliptical = hyperelliptical;
|
||
|
exports.geoHyperellipticalRaw = hyperellipticalRaw;
|
||
|
exports.geoInterrupt = interrupt;
|
||
|
exports.geoInterruptedBoggs = boggs$1;
|
||
|
exports.geoInterruptedHomolosine = homolosine$1;
|
||
|
exports.geoInterruptedMollweide = mollweide$1;
|
||
|
exports.geoInterruptedMollweideHemispheres = mollweideHemispheres;
|
||
|
exports.geoInterruptedSinuMollweide = sinuMollweide$1;
|
||
|
exports.geoInterruptedSinusoidal = sinusoidal$1;
|
||
|
exports.geoKavrayskiy7 = kavrayskiy7;
|
||
|
exports.geoKavrayskiy7Raw = kavrayskiy7Raw;
|
||
|
exports.geoLagrange = lagrange;
|
||
|
exports.geoLagrangeRaw = lagrangeRaw;
|
||
|
exports.geoLarrivee = larrivee;
|
||
|
exports.geoLarriveeRaw = larriveeRaw;
|
||
|
exports.geoLaskowski = laskowski;
|
||
|
exports.geoLaskowskiRaw = laskowskiRaw;
|
||
|
exports.geoLittrow = littrow;
|
||
|
exports.geoLittrowRaw = littrowRaw;
|
||
|
exports.geoLoximuthal = loximuthal;
|
||
|
exports.geoLoximuthalRaw = loximuthalRaw;
|
||
|
exports.geoMiller = miller;
|
||
|
exports.geoMillerRaw = millerRaw;
|
||
|
exports.geoModifiedStereographic = modifiedStereographic;
|
||
|
exports.geoModifiedStereographicRaw = modifiedStereographicRaw;
|
||
|
exports.geoModifiedStereographicAlaska = modifiedStereographicAlaska;
|
||
|
exports.geoModifiedStereographicGs48 = modifiedStereographicGs48;
|
||
|
exports.geoModifiedStereographicGs50 = modifiedStereographicGs50;
|
||
|
exports.geoModifiedStereographicMiller = modifiedStereographicMiller;
|
||
|
exports.geoModifiedStereographicLee = modifiedStereographicLee;
|
||
|
exports.geoMollweide = mollweide;
|
||
|
exports.geoMollweideRaw = mollweideRaw;
|
||
|
exports.geoMtFlatPolarParabolic = mtFlatPolarParabolic;
|
||
|
exports.geoMtFlatPolarParabolicRaw = mtFlatPolarParabolicRaw;
|
||
|
exports.geoMtFlatPolarQuartic = mtFlatPolarQuartic;
|
||
|
exports.geoMtFlatPolarQuarticRaw = mtFlatPolarQuarticRaw;
|
||
|
exports.geoMtFlatPolarSinusoidal = mtFlatPolarSinusoidal;
|
||
|
exports.geoMtFlatPolarSinusoidalRaw = mtFlatPolarSinusoidalRaw;
|
||
|
exports.geoNaturalEarth2 = naturalEarth2;
|
||
|
exports.geoNaturalEarth2Raw = naturalEarth2Raw;
|
||
|
exports.geoNellHammer = nellHammer;
|
||
|
exports.geoNellHammerRaw = nellHammerRaw;
|
||
|
exports.geoInterruptedQuarticAuthalic = quarticAuthalic;
|
||
|
exports.geoNicolosi = nicolosi;
|
||
|
exports.geoNicolosiRaw = nicolosiRaw;
|
||
|
exports.geoPatterson = patterson;
|
||
|
exports.geoPattersonRaw = pattersonRaw;
|
||
|
exports.geoPolyconic = polyconic;
|
||
|
exports.geoPolyconicRaw = polyconicRaw;
|
||
|
exports.geoPolyhedral = polyhedral;
|
||
|
exports.geoPolyhedralButterfly = butterfly;
|
||
|
exports.geoPolyhedralCollignon = collignon$1;
|
||
|
exports.geoPolyhedralWaterman = waterman;
|
||
|
exports.geoProject = index;
|
||
|
exports.geoGringortenQuincuncial = gringorten$1;
|
||
|
exports.geoPeirceQuincuncial = peirce;
|
||
|
exports.geoPierceQuincuncial = peirce;
|
||
|
exports.geoQuantize = quantize;
|
||
|
exports.geoQuincuncial = quincuncial;
|
||
|
exports.geoRectangularPolyconic = rectangularPolyconic;
|
||
|
exports.geoRectangularPolyconicRaw = rectangularPolyconicRaw;
|
||
|
exports.geoRobinson = robinson;
|
||
|
exports.geoRobinsonRaw = robinsonRaw;
|
||
|
exports.geoSatellite = satellite;
|
||
|
exports.geoSatelliteRaw = satelliteRaw;
|
||
|
exports.geoSinuMollweide = sinuMollweide;
|
||
|
exports.geoSinuMollweideRaw = sinuMollweideRaw;
|
||
|
exports.geoSinusoidal = sinusoidal;
|
||
|
exports.geoSinusoidalRaw = sinusoidalRaw;
|
||
|
exports.geoStitch = stitch;
|
||
|
exports.geoTimes = times;
|
||
|
exports.geoTimesRaw = timesRaw;
|
||
|
exports.geoTwoPointAzimuthal = twoPointAzimuthal;
|
||
|
exports.geoTwoPointAzimuthalRaw = twoPointAzimuthalRaw;
|
||
|
exports.geoTwoPointAzimuthalUsa = twoPointAzimuthalUsa;
|
||
|
exports.geoTwoPointEquidistant = twoPointEquidistant;
|
||
|
exports.geoTwoPointEquidistantRaw = twoPointEquidistantRaw;
|
||
|
exports.geoTwoPointEquidistantUsa = twoPointEquidistantUsa;
|
||
|
exports.geoVanDerGrinten = vanDerGrinten;
|
||
|
exports.geoVanDerGrintenRaw = vanDerGrintenRaw;
|
||
|
exports.geoVanDerGrinten2 = vanDerGrinten2;
|
||
|
exports.geoVanDerGrinten2Raw = vanDerGrinten2Raw;
|
||
|
exports.geoVanDerGrinten3 = vanDerGrinten3;
|
||
|
exports.geoVanDerGrinten3Raw = vanDerGrinten3Raw;
|
||
|
exports.geoVanDerGrinten4 = vanDerGrinten4;
|
||
|
exports.geoVanDerGrinten4Raw = vanDerGrinten4Raw;
|
||
|
exports.geoWagner = wagner;
|
||
|
exports.geoWagner7 = wagner7;
|
||
|
exports.geoWagnerRaw = wagnerRaw;
|
||
|
exports.geoWagner4 = wagner4;
|
||
|
exports.geoWagner4Raw = wagner4Raw;
|
||
|
exports.geoWagner6 = wagner6;
|
||
|
exports.geoWagner6Raw = wagner6Raw;
|
||
|
exports.geoWiechel = wiechel;
|
||
|
exports.geoWiechelRaw = wiechelRaw;
|
||
|
exports.geoWinkel3 = winkel3;
|
||
|
exports.geoWinkel3Raw = winkel3Raw;
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
||
|
})));
|