// https://d3js.org/d3-geo-projection/ v2.9.0 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 = sinz_2 / cosz_2, lnsecz_2 = -log(abs(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)) * (cosz_2 < 0 ? 0.7 : 1); } 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; if (abs(dPhi) > 2) dPhi /= 2; 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(y) * 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 }); })));