Created
September 22, 2015 12:47
-
-
Save pnavarrc/d920008a82f67ef2722d to your computer and use it in GitHub Desktop.
D3 Geo Projection Plugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
d3.geo.project = function(object, projection) { | |
var stream = projection.stream; | |
if (!stream) throw new Error("not yet supported"); | |
return (object && d3_geo_projectObjectType.hasOwnProperty(object.type) ? d3_geo_projectObjectType[object.type] : d3_geo_projectGeometry)(object, stream); | |
}; | |
function d3_geo_projectFeature(object, stream) { | |
return { | |
type: "Feature", | |
id: object.id, | |
properties: object.properties, | |
geometry: d3_geo_projectGeometry(object.geometry, stream) | |
}; | |
} | |
function d3_geo_projectGeometry(geometry, stream) { | |
if (!geometry) return null; | |
if (geometry.type === "GeometryCollection") return { | |
type: "GeometryCollection", | |
geometries: object.geometries.map(function(geometry) { | |
return d3_geo_projectGeometry(geometry, stream); | |
}) | |
}; | |
if (!d3_geo_projectGeometryType.hasOwnProperty(geometry.type)) return null; | |
var sink = d3_geo_projectGeometryType[geometry.type]; | |
d3.geo.stream(geometry, stream(sink)); | |
return sink.result(); | |
} | |
var d3_geo_projectObjectType = { | |
Feature: d3_geo_projectFeature, | |
FeatureCollection: function(object, stream) { | |
return { | |
type: "FeatureCollection", | |
features: object.features.map(function(feature) { | |
return d3_geo_projectFeature(feature, stream); | |
}) | |
}; | |
} | |
}; | |
var d3_geo_projectPoints = [], d3_geo_projectLines = []; | |
var d3_geo_projectPoint = { | |
point: function(x, y) { | |
d3_geo_projectPoints.push([ x, y ]); | |
}, | |
result: function() { | |
var result = !d3_geo_projectPoints.length ? null : d3_geo_projectPoints.length < 2 ? { | |
type: "Point", | |
coordinates: d3_geo_projectPoints[0] | |
} : { | |
type: "MultiPoint", | |
coordinates: d3_geo_projectPoints | |
}; | |
d3_geo_projectPoints = []; | |
return result; | |
} | |
}; | |
var d3_geo_projectLine = { | |
lineStart: d3_geo_projectNoop, | |
point: function(x, y) { | |
d3_geo_projectPoints.push([ x, y ]); | |
}, | |
lineEnd: function() { | |
if (d3_geo_projectPoints.length) d3_geo_projectLines.push(d3_geo_projectPoints), | |
d3_geo_projectPoints = []; | |
}, | |
result: function() { | |
var result = !d3_geo_projectLines.length ? null : d3_geo_projectLines.length < 2 ? { | |
type: "LineString", | |
coordinates: d3_geo_projectLines[0] | |
} : { | |
type: "MultiLineString", | |
coordinates: d3_geo_projectLines | |
}; | |
d3_geo_projectLines = []; | |
return result; | |
} | |
}; | |
var d3_geo_projectPolygon = { | |
polygonStart: d3_geo_projectNoop, | |
lineStart: d3_geo_projectNoop, | |
point: function(x, y) { | |
d3_geo_projectPoints.push([ x, y ]); | |
}, | |
lineEnd: function() { | |
var n = d3_geo_projectPoints.length; | |
if (n) { | |
do d3_geo_projectPoints.push(d3_geo_projectPoints[0].slice()); while (++n < 4); | |
d3_geo_projectLines.push(d3_geo_projectPoints), d3_geo_projectPoints = []; | |
} | |
}, | |
polygonEnd: d3_geo_projectNoop, | |
result: function() { | |
if (!d3_geo_projectLines.length) return null; | |
var polygons = [], holes = []; | |
d3_geo_projectLines.forEach(function(ring) { | |
if (d3_geo_projectClockwise(ring)) polygons.push([ ring ]); else holes.push(ring); | |
}); | |
holes.forEach(function(hole) { | |
var point = hole[0]; | |
polygons.some(function(polygon) { | |
if (d3_geo_projectContains(polygon[0], point)) { | |
polygon.push(hole); | |
return true; | |
} | |
}) || polygons.push([ hole ]); | |
}); | |
d3_geo_projectLines = []; | |
return !polygons.length ? null : polygons.length > 1 ? { | |
type: "MultiPolygon", | |
coordinates: polygons | |
} : { | |
type: "Polygon", | |
coordinates: polygons[0] | |
}; | |
} | |
}; | |
var d3_geo_projectGeometryType = { | |
Point: d3_geo_projectPoint, | |
MultiPoint: d3_geo_projectPoint, | |
LineString: d3_geo_projectLine, | |
MultiLineString: d3_geo_projectLine, | |
Polygon: d3_geo_projectPolygon, | |
MultiPolygon: d3_geo_projectPolygon, | |
Sphere: d3_geo_projectPolygon | |
}; | |
function d3_geo_projectNoop() {} | |
function d3_geo_projectClockwise(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 d3_geo_projectContains(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; | |
} | |
var ε = 1e-6, ε2 = ε * ε, π = Math.PI, halfπ = π / 2, sqrtπ = Math.sqrt(π), radians = π / 180, degrees = 180 / π; | |
function sinci(x) { | |
return x ? x / Math.sin(x) : 1; | |
} | |
function sgn(x) { | |
return x > 0 ? 1 : x < 0 ? -1 : 0; | |
} | |
function asin(x) { | |
return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); | |
} | |
function acos(x) { | |
return x > 1 ? 0 : x < -1 ? π : Math.acos(x); | |
} | |
function asqrt(x) { | |
return x > 0 ? Math.sqrt(x) : 0; | |
} | |
var projection = d3.geo.projection, projectionMutator = d3.geo.projectionMutator; | |
d3.geo.interrupt = function(project) { | |
var lobes = [ [ [ [ -π, 0 ], [ 0, halfπ ], [ π, 0 ] ] ], [ [ [ -π, 0 ], [ 0, -halfπ ], [ π, 0 ] ] ] ]; | |
var bounds; | |
function forward(λ, φ) { | |
var sign = φ < 0 ? -1 : +1, hemilobes = lobes[+(φ < 0)]; | |
for (var i = 0, n = hemilobes.length - 1; i < n && λ > hemilobes[i][2][0]; ++i) ; | |
var coordinates = project(λ - hemilobes[i][1][0], φ); | |
coordinates[0] += project(hemilobes[i][1][0], sign * φ > sign * hemilobes[i][0][1] ? hemilobes[i][0][1] : φ)[0]; | |
return coordinates; | |
} | |
function reset() { | |
bounds = lobes.map(function(hemilobes) { | |
return hemilobes.map(function(lobe) { | |
var x0 = project(lobe[0][0], lobe[0][1])[0], x1 = project(lobe[2][0], lobe[2][1])[0], y0 = project(lobe[1][0], lobe[0][1])[1], y1 = project(lobe[1][0], lobe[1][1])[1], t; | |
if (y0 > y1) t = y0, y0 = y1, y1 = t; | |
return [ [ x0, y0 ], [ x1, y1 ] ]; | |
}); | |
}); | |
} | |
if (project.invert) forward.invert = function(x, y) { | |
var hemibounds = bounds[+(y < 0)], hemilobes = lobes[+(y < 0)]; | |
for (var i = 0, n = hemibounds.length; i < n; ++i) { | |
var b = hemibounds[i]; | |
if (b[0][0] <= x && x < b[1][0] && b[0][1] <= y && y < b[1][1]) { | |
var coordinates = project.invert(x - project(hemilobes[i][1][0], 0)[0], y); | |
coordinates[0] += hemilobes[i][1][0]; | |
return pointEqual(forward(coordinates[0], coordinates[1]), [ x, y ]) ? coordinates : null; | |
} | |
} | |
}; | |
var projection = d3.geo.projection(forward), stream_ = projection.stream; | |
projection.stream = function(stream) { | |
var rotate = projection.rotate(), rotateStream = stream_(stream), sphereStream = (projection.rotate([ 0, 0 ]), | |
stream_(stream)); | |
projection.rotate(rotate); | |
rotateStream.sphere = function() { | |
d3.geo.stream(sphere(), sphereStream); | |
}; | |
return rotateStream; | |
}; | |
projection.lobes = function(_) { | |
if (!arguments.length) return lobes.map(function(lobes) { | |
return lobes.map(function(lobe) { | |
return [ [ lobe[0][0] * 180 / π, lobe[0][1] * 180 / π ], [ lobe[1][0] * 180 / π, lobe[1][1] * 180 / π ], [ lobe[2][0] * 180 / π, lobe[2][1] * 180 / π ] ]; | |
}); | |
}); | |
lobes = _.map(function(lobes) { | |
return lobes.map(function(lobe) { | |
return [ [ lobe[0][0] * π / 180, lobe[0][1] * π / 180 ], [ lobe[1][0] * π / 180, lobe[1][1] * π / 180 ], [ lobe[2][0] * π / 180, lobe[2][1] * π / 180 ] ]; | |
}); | |
}); | |
reset(); | |
return projection; | |
}; | |
function sphere() { | |
var ε = 1e-6, coordinates = []; | |
for (var i = 0, n = lobes[0].length; i < n; ++i) { | |
var lobe = lobes[0][i], λ0 = lobe[0][0] * 180 / π, φ0 = lobe[0][1] * 180 / π, φ1 = lobe[1][1] * 180 / π, λ2 = lobe[2][0] * 180 / π, φ2 = lobe[2][1] * 180 / π; | |
coordinates.push(resample([ [ λ0 + ε, φ0 + ε ], [ λ0 + ε, φ1 - ε ], [ λ2 - ε, φ1 - ε ], [ λ2 - ε, φ2 + ε ] ], 30)); | |
} | |
for (var i = lobes[1].length - 1; i >= 0; --i) { | |
var lobe = lobes[1][i], λ0 = lobe[0][0] * 180 / π, φ0 = lobe[0][1] * 180 / π, φ1 = lobe[1][1] * 180 / π, λ2 = lobe[2][0] * 180 / π, φ2 = lobe[2][1] * 180 / π; | |
coordinates.push(resample([ [ λ2 - ε, φ2 - ε ], [ λ2 - ε, φ1 + ε ], [ λ0 + ε, φ1 + ε ], [ λ0 + ε, φ0 - ε ] ], 30)); | |
} | |
return { | |
type: "Polygon", | |
coordinates: [ d3.merge(coordinates) ] | |
}; | |
} | |
function resample(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 pointEqual(a, b) { | |
return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; | |
} | |
return projection; | |
}; | |
function airy(β) { | |
var tanβ_2 = Math.tan(.5 * β), B = 2 * Math.log(Math.cos(.5 * β)) / (tanβ_2 * tanβ_2); | |
function forward(λ, φ) { | |
var cosλ = Math.cos(λ), cosφ = Math.cos(φ), sinφ = Math.sin(φ), cosz = cosφ * cosλ, K = -((1 - cosz ? Math.log(.5 * (1 + cosz)) / (1 - cosz) : -.5) + B / (1 + cosz)); | |
return [ K * cosφ * Math.sin(λ), K * sinφ ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ = Math.sqrt(x * x + y * y), z = β * -.5, i = 50, δ; | |
if (!ρ) return [ 0, 0 ]; | |
do { | |
var z_2 = .5 * z, cosz_2 = Math.cos(z_2), sinz_2 = Math.sin(z_2), tanz_2 = Math.tan(z_2), lnsecz_2 = Math.log(1 / cosz_2); | |
z -= δ = (2 / tanz_2 * lnsecz_2 - B * tanz_2 - ρ) / (-lnsecz_2 / (sinz_2 * sinz_2) + 1 - B / (2 * cosz_2 * cosz_2)); | |
} while (Math.abs(δ) > ε && --i > 0); | |
var sinz = Math.sin(z); | |
return [ Math.atan2(x * sinz, ρ * Math.cos(z)), asin(y * sinz / ρ) ]; | |
}; | |
return forward; | |
} | |
function airyProjection() { | |
var β = halfπ, m = projectionMutator(airy), p = m(β); | |
p.radius = function(_) { | |
if (!arguments.length) return β / π * 180; | |
return m(β = _ * π / 180); | |
}; | |
return p; | |
} | |
(d3.geo.airy = airyProjection).raw = airy; | |
function aitoff(λ, φ) { | |
var cosφ = Math.cos(φ), sinciα = sinci(acos(cosφ * Math.cos(λ /= 2))); | |
return [ 2 * cosφ * Math.sin(λ) * sinciα, Math.sin(φ) * sinciα ]; | |
} | |
aitoff.invert = function(x, y) { | |
if (x * x + 4 * y * y > π * π + ε) return; | |
var λ = x, φ = y, i = 25; | |
do { | |
var sinλ = Math.sin(λ), sinλ_2 = Math.sin(λ / 2), cosλ_2 = Math.cos(λ / 2), sinφ = Math.sin(φ), cosφ = Math.cos(φ), sin_2φ = Math.sin(2 * φ), sin2φ = sinφ * sinφ, cos2φ = cosφ * cosφ, sin2λ_2 = sinλ_2 * sinλ_2, C = 1 - cos2φ * cosλ_2 * cosλ_2, E = C ? acos(cosφ * cosλ_2) * Math.sqrt(F = 1 / C) : F = 0, F, fx = 2 * E * cosφ * sinλ_2 - x, fy = E * sinφ - y, δxδλ = F * (cos2φ * sin2λ_2 + E * cosφ * cosλ_2 * sin2φ), δxδφ = F * (.5 * sinλ * sin_2φ - E * 2 * sinφ * sinλ_2), δyδλ = F * .25 * (sin_2φ * sinλ_2 - E * sinφ * cos2φ * sinλ), δyδφ = F * (sin2φ * cosλ_2 + E * sin2λ_2 * cosφ), denominator = δxδφ * δyδλ - δyδφ * δxδλ; | |
if (!denominator) break; | |
var δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
λ -= δλ, φ -= δφ; | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return [ λ, φ ]; | |
}; | |
(d3.geo.aitoff = function() { | |
return projection(aitoff); | |
}).raw = aitoff; | |
function armadillo(φ0) { | |
var sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sφ0 = φ0 > 0 ? 1 : -1, tanφ0 = Math.tan(sφ0 * φ0), k = (1 + sinφ0 - cosφ0) / 2; | |
function forward(λ, φ) { | |
var cosφ = Math.cos(φ), cosλ = Math.cos(λ /= 2); | |
return [ (1 + cosφ) * Math.sin(λ), (sφ0 * φ > -Math.atan2(cosλ, tanφ0) - .001 ? 0 : -sφ0 * 10) + k + Math.sin(φ) * cosφ0 - (1 + cosφ) * sinφ0 * cosλ ]; | |
} | |
forward.invert = function(x, y) { | |
var λ = 0, φ = 0, i = 50; | |
do { | |
var cosλ = Math.cos(λ), sinλ = Math.sin(λ), cosφ = Math.cos(φ), sinφ = Math.sin(φ), A = 1 + cosφ, fx = A * sinλ - x, fy = k + sinφ * cosφ0 - A * sinφ0 * cosλ - y, δxδλ = .5 * A * cosλ, δxδφ = -sinλ * sinφ, δyδλ = .5 * sinφ0 * A * sinλ, δyδφ = cosφ0 * cosφ + sinφ0 * cosλ * sinφ, denominator = δxδφ * δyδλ - δyδφ * δxδλ, δλ = .5 * (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
λ -= δλ, φ -= δφ; | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return sφ0 * φ > -Math.atan2(Math.cos(λ), tanφ0) - .001 ? [ λ * 2, φ ] : null; | |
}; | |
return forward; | |
} | |
function armadilloProjection() { | |
var φ0 = π / 9, sφ0 = φ0 > 0 ? 1 : -1, tanφ0 = Math.tan(sφ0 * φ0), m = projectionMutator(armadillo), p = m(φ0), stream_ = p.stream; | |
p.parallel = function(_) { | |
if (!arguments.length) return φ0 / π * 180; | |
tanφ0 = Math.tan((sφ0 = (φ0 = _ * π / 180) > 0 ? 1 : -1) * φ0); | |
return m(φ0); | |
}; | |
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 λ = sφ0 * -180; sφ0 * λ < 180; λ += sφ0 * 90) sphereStream.point(λ, sφ0 * 90); | |
while (sφ0 * (λ -= φ0) >= -180) { | |
sphereStream.point(λ, sφ0 * -Math.atan2(Math.cos(λ * radians / 2), tanφ0) * degrees); | |
} | |
sphereStream.lineEnd(), sphereStream.polygonEnd(); | |
}; | |
return rotateStream; | |
}; | |
return p; | |
} | |
(d3.geo.armadillo = armadilloProjection).raw = armadillo; | |
function tanh(x) { | |
x = Math.exp(2 * x); | |
return (x - 1) / (x + 1); | |
} | |
function sinh(x) { | |
return .5 * (Math.exp(x) - Math.exp(-x)); | |
} | |
function cosh(x) { | |
return .5 * (Math.exp(x) + Math.exp(-x)); | |
} | |
function arsinh(x) { | |
return Math.log(x + asqrt(x * x + 1)); | |
} | |
function arcosh(x) { | |
return Math.log(x + asqrt(x * x - 1)); | |
} | |
function august(λ, φ) { | |
var tanφ = Math.tan(φ / 2), k = asqrt(1 - tanφ * tanφ), c = 1 + k * Math.cos(λ /= 2), x = Math.sin(λ) * k / c, y = tanφ / c, x2 = x * x, y2 = y * y; | |
return [ 4 / 3 * x * (3 + x2 - 3 * y2), 4 / 3 * y * (3 + 3 * x2 - y2) ]; | |
} | |
august.invert = function(x, y) { | |
x *= 3 / 8, y *= 3 / 8; | |
if (!x && Math.abs(y) > 1) return null; | |
var x2 = x * x, y2 = y * y, s = 1 + x2 + y2, sin3η = Math.sqrt(.5 * (s - Math.sqrt(s * s - 4 * y * y))), η = asin(sin3η) / 3, ξ = sin3η ? arcosh(Math.abs(y / sin3η)) / 3 : arsinh(Math.abs(x)) / 3, cosη = Math.cos(η), coshξ = cosh(ξ), d = coshξ * coshξ - cosη * cosη; | |
return [ sgn(x) * 2 * Math.atan2(sinh(ξ) * cosη, .25 - d), sgn(y) * 2 * Math.atan2(coshξ * Math.sin(η), .25 + d) ]; | |
}; | |
(d3.geo.august = function() { | |
return projection(august); | |
}).raw = august; | |
var bakerφ = Math.log(1 + Math.SQRT2); | |
function baker(λ, φ) { | |
var φ0 = Math.abs(φ); | |
return φ0 < π / 4 ? [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ] : [ λ * Math.cos(φ0) * (2 * Math.SQRT2 - 1 / Math.sin(φ0)), sgn(φ) * (2 * Math.SQRT2 * (φ0 - π / 4) - Math.log(Math.tan(φ0 / 2))) ]; | |
} | |
baker.invert = function(x, y) { | |
if ((y0 = Math.abs(y)) < bakerφ) return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; | |
var sqrt8 = Math.sqrt(8), φ = π / 4, i = 25, δ, y0; | |
do { | |
var cosφ_2 = Math.cos(φ / 2), tanφ_2 = Math.tan(φ / 2); | |
φ -= δ = (sqrt8 * (φ - π / 4) - Math.log(tanφ_2) - y0) / (sqrt8 - .5 * cosφ_2 * cosφ_2 / tanφ_2); | |
} while (Math.abs(δ) > ε2 && --i > 0); | |
return [ x / (Math.cos(φ) * (sqrt8 - 1 / Math.sin(φ))), sgn(y) * φ ]; | |
}; | |
(d3.geo.baker = function() { | |
return projection(baker); | |
}).raw = baker; | |
var berghausAzimuthalEquidistant = d3.geo.azimuthalEquidistant.raw; | |
function berghaus(n) { | |
var k = 2 * π / n; | |
function forward(λ, φ) { | |
var p = berghausAzimuthalEquidistant(λ, φ); | |
if (Math.abs(λ) > halfπ) { | |
var θ = Math.atan2(p[1], p[0]), r = Math.sqrt(p[0] * p[0] + p[1] * p[1]), θ0 = k * Math.round((θ - halfπ) / k) + halfπ, α = Math.atan2(Math.sin(θ -= θ0), 2 - Math.cos(θ)); | |
θ = θ0 + asin(π / r * Math.sin(α)) - α; | |
p[0] = r * Math.cos(θ); | |
p[1] = r * Math.sin(θ); | |
} | |
return p; | |
} | |
forward.invert = function(x, y) { | |
var r = Math.sqrt(x * x + y * y); | |
if (r > halfπ) { | |
var θ = Math.atan2(y, x), θ0 = k * Math.round((θ - halfπ) / k) + halfπ, s = θ > θ0 ? -1 : 1, A = r * Math.cos(θ0 - θ), cotα = 1 / Math.tan(s * Math.acos((A - π) / Math.sqrt(π * (π - 2 * A) + r * r))); | |
θ = θ0 + 2 * Math.atan((cotα + s * Math.sqrt(cotα * cotα - 3)) / 3); | |
x = r * Math.cos(θ), y = r * Math.sin(θ); | |
} | |
return berghausAzimuthalEquidistant.invert(x, y); | |
}; | |
return forward; | |
} | |
function berghausProjection() { | |
var n = 5, m = projectionMutator(berghaus), p = m(n), stream_ = p.stream, ε = .01, cr = -Math.cos(ε * radians), sr = Math.sin(ε * radians); | |
p.lobes = function(_) { | |
if (!arguments.length) return n; | |
return m(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, δ = 360 / n, δ0 = 2 * π / n, φ = 90 - 180 / n, φ0 = halfπ; i < n; ++i, | |
φ -= δ, φ0 -= δ0) { | |
sphereStream.point(Math.atan2(sr * Math.cos(φ0), cr) * degrees, asin(sr * Math.sin(φ0)) * degrees); | |
if (φ < -90) { | |
sphereStream.point(-90, -180 - φ - ε); | |
sphereStream.point(-90, -180 - φ + ε); | |
} else { | |
sphereStream.point(90, φ + ε); | |
sphereStream.point(90, φ - ε); | |
} | |
} | |
sphereStream.lineEnd(), sphereStream.polygonEnd(); | |
}; | |
return rotateStream; | |
}; | |
return p; | |
} | |
(d3.geo.berghaus = berghausProjection).raw = berghaus; | |
function mollweideBromleyθ(Cp) { | |
return function(θ) { | |
var Cpsinθ = Cp * Math.sin(θ), i = 30, δ; | |
do θ -= δ = (θ + Math.sin(θ) - Cpsinθ) / (1 + Math.cos(θ)); while (Math.abs(δ) > ε && --i > 0); | |
return θ / 2; | |
}; | |
} | |
function mollweideBromley(Cx, Cy, Cp) { | |
var θ = mollweideBromleyθ(Cp); | |
function forward(λ, φ) { | |
return [ Cx * λ * Math.cos(φ = θ(φ)), Cy * Math.sin(φ) ]; | |
} | |
forward.invert = function(x, y) { | |
var θ = asin(y / Cy); | |
return [ x / (Cx * Math.cos(θ)), asin((2 * θ + Math.sin(2 * θ)) / Cp) ]; | |
}; | |
return forward; | |
} | |
var mollweideθ = mollweideBromleyθ(π), mollweide = mollweideBromley(Math.SQRT2 / halfπ, Math.SQRT2, π); | |
(d3.geo.mollweide = function() { | |
return projection(mollweide); | |
}).raw = mollweide; | |
function boggs(λ, φ) { | |
var k = 2.00276, θ = mollweideθ(φ); | |
return [ k * λ / (1 / Math.cos(φ) + 1.11072 / Math.cos(θ)), (φ + Math.SQRT2 * Math.sin(θ)) / k ]; | |
} | |
boggs.invert = function(x, y) { | |
var k = 2.00276, ky = k * y, θ = y < 0 ? -π / 4 : π / 4, i = 25, δ, φ; | |
do { | |
φ = ky - Math.SQRT2 * Math.sin(θ); | |
θ -= δ = (Math.sin(2 * θ) + 2 * θ - π * Math.sin(φ)) / (2 * Math.cos(2 * θ) + 2 + π * Math.cos(φ) * Math.SQRT2 * Math.cos(θ)); | |
} while (Math.abs(δ) > ε && --i > 0); | |
φ = ky - Math.SQRT2 * Math.sin(θ); | |
return [ x * (1 / Math.cos(φ) + 1.11072 / Math.cos(θ)) / k, φ ]; | |
}; | |
(d3.geo.boggs = function() { | |
return projection(boggs); | |
}).raw = boggs; | |
function parallel1Projection(projectAt) { | |
var φ0 = 0, m = projectionMutator(projectAt), p = m(φ0); | |
p.parallel = function(_) { | |
if (!arguments.length) return φ0 / π * 180; | |
return m(φ0 = _ * π / 180); | |
}; | |
return p; | |
} | |
function sinusoidal(λ, φ) { | |
return [ λ * Math.cos(φ), φ ]; | |
} | |
sinusoidal.invert = function(x, y) { | |
return [ x / Math.cos(y), y ]; | |
}; | |
(d3.geo.sinusoidal = function() { | |
return projection(sinusoidal); | |
}).raw = sinusoidal; | |
function bonne(φ0) { | |
if (!φ0) return sinusoidal; | |
var cotφ0 = 1 / Math.tan(φ0); | |
function forward(λ, φ) { | |
var ρ = cotφ0 + φ0 - φ, E = ρ ? λ * Math.cos(φ) / ρ : ρ; | |
return [ ρ * Math.sin(E), cotφ0 - ρ * Math.cos(E) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ = Math.sqrt(x * x + (y = cotφ0 - y) * y), φ = cotφ0 + φ0 - ρ; | |
return [ ρ / Math.cos(φ) * Math.atan2(x, y), φ ]; | |
}; | |
return forward; | |
} | |
(d3.geo.bonne = function() { | |
return parallel1Projection(bonne).parallel(45); | |
}).raw = bonne; | |
var bromley = mollweideBromley(1, 4 / π, π); | |
(d3.geo.bromley = function() { | |
return projection(bromley); | |
}).raw = bromley; | |
function chamberlin(points) { | |
points = points.map(function(p) { | |
return [ p[0], p[1], Math.sin(p[1]), Math.cos(p[1]) ]; | |
}); | |
for (var a = points[2], b, i = 0; i < 3; ++i, a = b) { | |
b = points[i]; | |
a.v = chamberlinDistanceAzimuth(b[1] - a[1], a[3], a[2], b[3], b[2], b[0] - a[0]); | |
a.point = [ 0, 0 ]; | |
} | |
var β0 = chamberlinAngle(points[0].v[0], points[2].v[0], points[1].v[0]), β1 = chamberlinAngle(points[0].v[0], points[1].v[0], points[2].v[0]), β2 = π - β0; | |
points[2].point[1] = 0; | |
points[0].point[0] = -(points[1].point[0] = .5 * points[0].v[0]); | |
var mean = [ points[2].point[0] = points[0].point[0] + points[2].v[0] * Math.cos(β0), 2 * (points[0].point[1] = points[1].point[1] = points[2].v[0] * Math.sin(β0)) ]; | |
function forward(λ, φ) { | |
var sinφ = Math.sin(φ), cosφ = Math.cos(φ), v = new Array(3); | |
for (var i = 0; i < 3; ++i) { | |
var p = points[i]; | |
v[i] = chamberlinDistanceAzimuth(φ - p[1], p[3], p[2], cosφ, sinφ, λ - p[0]); | |
if (!v[i][0]) return p.point; | |
v[i][1] = chamberlinLongitude(v[i][1] - p.v[1]); | |
} | |
var point = mean.slice(); | |
for (var i = 0; i < 3; ++i) { | |
var j = i == 2 ? 0 : i + 1; | |
var a = chamberlinAngle(points[i].v[0], v[i][0], v[j][0]); | |
if (v[i][1] < 0) a = -a; | |
if (!i) { | |
point[0] += v[i][0] * Math.cos(a); | |
point[1] -= v[i][0] * Math.sin(a); | |
} else if (i == 1) { | |
a = β1 - a; | |
point[0] -= v[i][0] * Math.cos(a); | |
point[1] -= v[i][0] * Math.sin(a); | |
} else { | |
a = β2 - a; | |
point[0] += v[i][0] * Math.cos(a); | |
point[1] += v[i][0] * Math.sin(a); | |
} | |
} | |
point[0] /= 3, point[1] /= 3; | |
return point; | |
} | |
return forward; | |
} | |
function chamberlinProjection() { | |
var points = [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ], m = projectionMutator(chamberlin), p = m(points), rotate = p.rotate; | |
delete p.rotate; | |
p.points = function(_) { | |
if (!arguments.length) return points; | |
points = _; | |
var origin = d3.geo.centroid({ | |
type: "MultiPoint", | |
coordinates: points | |
}), r = [ -origin[0], -origin[1] ]; | |
rotate.call(p, r); | |
return m(points.map(d3.geo.rotation(r)).map(chamberlinRadians)); | |
}; | |
return p.points([ [ -150, 55 ], [ -35, 55 ], [ -92.5, 10 ] ]); | |
} | |
function chamberlinDistanceAzimuth(dφ, c1, s1, c2, s2, dλ) { | |
var cosdλ = Math.cos(dλ), r; | |
if (Math.abs(dφ) > 1 || Math.abs(dλ) > 1) { | |
r = acos(s1 * s2 + c1 * c2 * cosdλ); | |
} else { | |
var sindφ = Math.sin(.5 * dφ), sindλ = Math.sin(.5 * dλ); | |
r = 2 * asin(Math.sqrt(sindφ * sindφ + c1 * c2 * sindλ * sindλ)); | |
} | |
if (Math.abs(r) > ε) { | |
return [ r, Math.atan2(c2 * Math.sin(dλ), c1 * s2 - s1 * c2 * cosdλ) ]; | |
} | |
return [ 0, 0 ]; | |
} | |
function chamberlinAngle(b, c, a) { | |
return acos(.5 * (b * b + c * c - a * a) / (b * c)); | |
} | |
function chamberlinLongitude(λ) { | |
return λ - 2 * π * Math.floor((λ + π) / (2 * π)); | |
} | |
function chamberlinRadians(point) { | |
return [ point[0] * radians, point[1] * radians ]; | |
} | |
(d3.geo.chamberlin = chamberlinProjection).raw = chamberlin; | |
function collignon(λ, φ) { | |
var α = asqrt(1 - Math.sin(φ)); | |
return [ 2 / sqrtπ * λ * α, sqrtπ * (1 - α) ]; | |
} | |
collignon.invert = function(x, y) { | |
var λ = (λ = y / sqrtπ - 1) * λ; | |
return [ λ > 0 ? x * Math.sqrt(π / λ) / 2 : 0, asin(1 - λ) ]; | |
}; | |
(d3.geo.collignon = function() { | |
return projection(collignon); | |
}).raw = collignon; | |
function craig(φ0) { | |
var tanφ0 = Math.tan(φ0); | |
function forward(λ, φ) { | |
return [ λ, (λ ? λ / Math.sin(λ) : 1) * (Math.sin(φ) * Math.cos(λ) - tanφ0 * Math.cos(φ)) ]; | |
} | |
forward.invert = tanφ0 ? function(x, y) { | |
if (x) y *= Math.sin(x) / x; | |
var cosλ = Math.cos(x); | |
return [ x, 2 * Math.atan2(Math.sqrt(cosλ * cosλ + tanφ0 * tanφ0 - y * y) - cosλ, tanφ0 - y) ]; | |
} : function(x, y) { | |
return [ x, asin(x ? y * Math.tan(x) / x : y) ]; | |
}; | |
return forward; | |
} | |
(d3.geo.craig = function() { | |
return parallel1Projection(craig); | |
}).raw = craig; | |
function craster(λ, φ) { | |
var sqrt3 = Math.sqrt(3); | |
return [ sqrt3 * λ * (2 * Math.cos(2 * φ / 3) - 1) / sqrtπ, sqrt3 * sqrtπ * Math.sin(φ / 3) ]; | |
} | |
craster.invert = function(x, y) { | |
var sqrt3 = Math.sqrt(3), φ = 3 * asin(y / (sqrt3 * sqrtπ)); | |
return [ sqrtπ * x / (sqrt3 * (2 * Math.cos(2 * φ / 3) - 1)), φ ]; | |
}; | |
(d3.geo.craster = function() { | |
return projection(craster); | |
}).raw = craster; | |
function cylindricalEqualArea(φ0) { | |
var cosφ0 = Math.cos(φ0); | |
function forward(λ, φ) { | |
return [ λ * cosφ0, Math.sin(φ) / cosφ0 ]; | |
} | |
forward.invert = function(x, y) { | |
return [ x / cosφ0, asin(y * cosφ0) ]; | |
}; | |
return forward; | |
} | |
(d3.geo.cylindricalEqualArea = function() { | |
return parallel1Projection(cylindricalEqualArea); | |
}).raw = cylindricalEqualArea; | |
function cylindricalStereographic(φ0) { | |
var cosφ0 = Math.cos(φ0); | |
function forward(λ, φ) { | |
return [ λ * cosφ0, (1 + cosφ0) * Math.tan(φ * .5) ]; | |
} | |
forward.invert = function(x, y) { | |
return [ x / cosφ0, Math.atan(y / (1 + cosφ0)) * 2 ]; | |
}; | |
return forward; | |
} | |
(d3.geo.cylindricalStereographic = function() { | |
return parallel1Projection(cylindricalStereographic); | |
}).raw = cylindricalStereographic; | |
function eckert1(λ, φ) { | |
var α = Math.sqrt(8 / (3 * π)); | |
return [ α * λ * (1 - Math.abs(φ) / π), α * φ ]; | |
} | |
eckert1.invert = function(x, y) { | |
var α = Math.sqrt(8 / (3 * π)), φ = y / α; | |
return [ x / (α * (1 - Math.abs(φ) / π)), φ ]; | |
}; | |
(d3.geo.eckert1 = function() { | |
return projection(eckert1); | |
}).raw = eckert1; | |
function eckert2(λ, φ) { | |
var α = Math.sqrt(4 - 3 * Math.sin(Math.abs(φ))); | |
return [ 2 / Math.sqrt(6 * π) * λ * α, sgn(φ) * Math.sqrt(2 * π / 3) * (2 - α) ]; | |
} | |
eckert2.invert = function(x, y) { | |
var α = 2 - Math.abs(y) / Math.sqrt(2 * π / 3); | |
return [ x * Math.sqrt(6 * π) / (2 * α), sgn(y) * asin((4 - α * α) / 3) ]; | |
}; | |
(d3.geo.eckert2 = function() { | |
return projection(eckert2); | |
}).raw = eckert2; | |
function eckert3(λ, φ) { | |
var k = Math.sqrt(π * (4 + π)); | |
return [ 2 / k * λ * (1 + Math.sqrt(1 - 4 * φ * φ / (π * π))), 4 / k * φ ]; | |
} | |
eckert3.invert = function(x, y) { | |
var k = Math.sqrt(π * (4 + π)) / 2; | |
return [ x * k / (1 + asqrt(1 - y * y * (4 + π) / (4 * π))), y * k / 2 ]; | |
}; | |
(d3.geo.eckert3 = function() { | |
return projection(eckert3); | |
}).raw = eckert3; | |
function eckert4(λ, φ) { | |
var k = (2 + halfπ) * Math.sin(φ); | |
φ /= 2; | |
for (var i = 0, δ = Infinity; i < 10 && Math.abs(δ) > ε; i++) { | |
var cosφ = Math.cos(φ); | |
φ -= δ = (φ + Math.sin(φ) * (cosφ + 2) - k) / (2 * cosφ * (1 + cosφ)); | |
} | |
return [ 2 / Math.sqrt(π * (4 + π)) * λ * (1 + Math.cos(φ)), 2 * Math.sqrt(π / (4 + π)) * Math.sin(φ) ]; | |
} | |
eckert4.invert = function(x, y) { | |
var A = .5 * y * Math.sqrt((4 + π) / π), k = asin(A), c = Math.cos(k); | |
return [ x / (2 / Math.sqrt(π * (4 + π)) * (1 + c)), asin((k + A * (c + 2)) / (2 + halfπ)) ]; | |
}; | |
(d3.geo.eckert4 = function() { | |
return projection(eckert4); | |
}).raw = eckert4; | |
function eckert5(λ, φ) { | |
return [ λ * (1 + Math.cos(φ)) / Math.sqrt(2 + π), 2 * φ / Math.sqrt(2 + π) ]; | |
} | |
eckert5.invert = function(x, y) { | |
var k = Math.sqrt(2 + π), φ = y * k / 2; | |
return [ k * x / (1 + Math.cos(φ)), φ ]; | |
}; | |
(d3.geo.eckert5 = function() { | |
return projection(eckert5); | |
}).raw = eckert5; | |
function eckert6(λ, φ) { | |
var k = (1 + halfπ) * Math.sin(φ); | |
for (var i = 0, δ = Infinity; i < 10 && Math.abs(δ) > ε; i++) { | |
φ -= δ = (φ + Math.sin(φ) - k) / (1 + Math.cos(φ)); | |
} | |
k = Math.sqrt(2 + π); | |
return [ λ * (1 + Math.cos(φ)) / k, 2 * φ / k ]; | |
} | |
eckert6.invert = function(x, y) { | |
var j = 1 + halfπ, k = Math.sqrt(j / 2); | |
return [ x * 2 * k / (1 + Math.cos(y *= k)), asin((y + Math.sin(y)) / j) ]; | |
}; | |
(d3.geo.eckert6 = function() { | |
return projection(eckert6); | |
}).raw = eckert6; | |
function eisenlohr(λ, φ) { | |
var s0 = Math.sin(λ /= 2), c0 = Math.cos(λ), k = Math.sqrt(Math.cos(φ)), c1 = Math.cos(φ /= 2), t = Math.sin(φ) / (c1 + Math.SQRT2 * c0 * k), c = Math.sqrt(2 / (1 + t * t)), v = Math.sqrt((Math.SQRT2 * c1 + (c0 + s0) * k) / (Math.SQRT2 * c1 + (c0 - s0) * k)); | |
return [ eisenlohrK * (c * (v - 1 / v) - 2 * Math.log(v)), eisenlohrK * (c * t * (v + 1 / v) - 2 * Math.atan(t)) ]; | |
} | |
eisenlohr.invert = function(x, y) { | |
var p = d3.geo.august.raw.invert(x / 1.2, y * 1.065); | |
if (!p) return null; | |
var λ = p[0], φ = p[1], i = 20; | |
x /= eisenlohrK, y /= eisenlohrK; | |
do { | |
var _0 = λ / 2, _1 = φ / 2, s0 = Math.sin(_0), c0 = Math.cos(_0), s1 = Math.sin(_1), c1 = Math.cos(_1), cos1 = Math.cos(φ), k = Math.sqrt(cos1), t = s1 / (c1 + Math.SQRT2 * c0 * k), t2 = t * t, c = Math.sqrt(2 / (1 + t2)), v0 = Math.SQRT2 * c1 + (c0 + s0) * k, v1 = Math.SQRT2 * c1 + (c0 - s0) * k, v2 = v0 / v1, v = Math.sqrt(v2), vm1v = v - 1 / v, vp1v = v + 1 / v, fx = c * vm1v - 2 * Math.log(v) - x, fy = c * t * vp1v - 2 * Math.atan(t) - y, δtδλ = s1 && Math.SQRT1_2 * k * s0 * t2 / s1, δtδφ = (Math.SQRT2 * c0 * c1 + k) / (2 * (c1 + Math.SQRT2 * c0 * k) * (c1 + Math.SQRT2 * c0 * k) * k), δcδt = -.5 * t * c * c * c, δcδλ = δcδt * δtδλ, δcδφ = δcδt * δtδφ, A = (A = 2 * c1 + Math.SQRT2 * k * (c0 - s0)) * A * v, δvδλ = (Math.SQRT2 * c0 * c1 * k + cos1) / A, δvδφ = -(Math.SQRT2 * s0 * s1) / (k * A), δxδλ = vm1v * δcδλ - 2 * δvδλ / v + c * (δvδλ + δvδλ / v2), δxδφ = vm1v * δcδφ - 2 * δvδφ / v + c * (δvδφ + δvδφ / v2), δyδλ = t * vp1v * δcδλ - 2 * δtδλ / (1 + t2) + c * vp1v * δtδλ + c * t * (δvδλ - δvδλ / v2), δyδφ = t * vp1v * δcδφ - 2 * δtδφ / (1 + t2) + c * vp1v * δtδφ + c * t * (δvδφ - δvδφ / v2), denominator = δxδφ * δyδλ - δyδφ * δxδλ; | |
if (!denominator) break; | |
var δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
λ -= δλ; | |
φ = Math.max(-halfπ, Math.min(halfπ, φ - δφ)); | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return Math.abs(Math.abs(φ) - halfπ) < ε ? [ 0, φ ] : i && [ λ, φ ]; | |
}; | |
var eisenlohrK = 3 + 2 * Math.SQRT2; | |
(d3.geo.eisenlohr = function() { | |
return projection(eisenlohr); | |
}).raw = eisenlohr; | |
function fahey(λ, φ) { | |
var t = Math.tan(φ / 2); | |
return [ λ * faheyK * asqrt(1 - t * t), (1 + faheyK) * t ]; | |
} | |
fahey.invert = function(x, y) { | |
var t = y / (1 + faheyK); | |
return [ x ? x / (faheyK * asqrt(1 - t * t)) : 0, 2 * Math.atan(t) ]; | |
}; | |
var faheyK = Math.cos(35 * radians); | |
(d3.geo.fahey = function() { | |
return projection(fahey); | |
}).raw = fahey; | |
function foucaut(λ, φ) { | |
var k = φ / 2, cosk = Math.cos(k); | |
return [ 2 * λ / sqrtπ * Math.cos(φ) * cosk * cosk, sqrtπ * Math.tan(k) ]; | |
} | |
foucaut.invert = function(x, y) { | |
var k = Math.atan(y / sqrtπ), cosk = Math.cos(k), φ = 2 * k; | |
return [ x * sqrtπ * .5 / (Math.cos(φ) * cosk * cosk), φ ]; | |
}; | |
(d3.geo.foucaut = function() { | |
return projection(foucaut); | |
}).raw = foucaut; | |
d3.geo.gilbert = function(projection) { | |
var e = d3.geo.equirectangular().scale(degrees).translate([ 0, 0 ]); | |
function gilbert(coordinates) { | |
return projection([ coordinates[0] * .5, asin(Math.tan(coordinates[1] * .5 * radians)) * degrees ]); | |
} | |
if (projection.invert) gilbert.invert = function(coordinates) { | |
coordinates = projection.invert(coordinates); | |
coordinates[0] *= 2; | |
coordinates[1] = 2 * Math.atan(Math.sin(coordinates[1] * radians)) * degrees; | |
return coordinates; | |
}; | |
gilbert.stream = function(stream) { | |
stream = projection.stream(stream); | |
var s = e.stream({ | |
point: function(λ, φ) { | |
stream.point(λ * .5, asin(Math.tan(-φ * .5 * radians)) * degrees); | |
}, | |
lineStart: function() { | |
stream.lineStart(); | |
}, | |
lineEnd: function() { | |
stream.lineEnd(); | |
}, | |
polygonStart: function() { | |
stream.polygonStart(); | |
}, | |
polygonEnd: function() { | |
stream.polygonEnd(); | |
} | |
}); | |
s.sphere = function() { | |
stream.sphere(); | |
}; | |
s.valid = false; | |
return s; | |
}; | |
return gilbert; | |
}; | |
var gingeryAzimuthalEquidistant = d3.geo.azimuthalEquidistant.raw; | |
function gingery(ρ, n) { | |
var k = 2 * π / n, ρ2 = ρ * ρ; | |
function forward(λ, φ) { | |
var p = gingeryAzimuthalEquidistant(λ, φ), x = p[0], y = p[1], r2 = x * x + y * y; | |
if (r2 > ρ2) { | |
var r = Math.sqrt(r2), θ = Math.atan2(y, x), θ0 = k * Math.round(θ / k), α = θ - θ0, ρcosα = ρ * Math.cos(α), k_ = (ρ * Math.sin(α) - α * Math.sin(ρcosα)) / (halfπ - ρcosα), s_ = arcLength_(α, k_), e = (π - ρ) / gingeryIntegrate(s_, ρcosα, π); | |
x = r; | |
var i = 50, δ; | |
do { | |
x -= δ = (ρ + gingeryIntegrate(s_, ρcosα, x) * e - r) / (s_(x) * e); | |
} while (Math.abs(δ) > ε && --i > 0); | |
y = α * Math.sin(x); | |
if (x < halfπ) y -= k_ * (x - halfπ); | |
var s = Math.sin(θ0), c = Math.cos(θ0); | |
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 > ρ2) { | |
var r = Math.sqrt(r2), θ = Math.atan2(y, x), θ0 = k * Math.round(θ / k), dθ = θ - θ0, x = r * Math.cos(dθ); | |
y = r * Math.sin(dθ); | |
var x_halfπ = x - halfπ, sinx = Math.sin(x), α = y / sinx, δ = x < halfπ ? Infinity : 0, i = 10; | |
while (true) { | |
var ρsinα = ρ * Math.sin(α), ρcosα = ρ * Math.cos(α), sinρcosα = Math.sin(ρcosα), halfπ_ρcosα = halfπ - ρcosα, k_ = (ρsinα - α * sinρcosα) / halfπ_ρcosα, s_ = arcLength_(α, k_); | |
if (Math.abs(δ) < ε2 || !--i) break; | |
α -= δ = (α * sinx - k_ * x_halfπ - y) / (sinx - x_halfπ * 2 * (halfπ_ρcosα * (ρcosα + α * ρsinα * Math.cos(ρcosα) - sinρcosα) - ρsinα * (ρsinα - α * sinρcosα)) / (halfπ_ρcosα * halfπ_ρcosα)); | |
} | |
r = ρ + gingeryIntegrate(s_, ρcosα, x) * (π - ρ) / gingeryIntegrate(s_, ρcosα, π); | |
θ = θ0 + α; | |
x = r * Math.cos(θ); | |
y = r * Math.sin(θ); | |
} | |
return gingeryAzimuthalEquidistant.invert(x, y); | |
}; | |
return forward; | |
} | |
function arcLength_(α, k) { | |
return function(x) { | |
var y_ = α * Math.cos(x); | |
if (x < halfπ) y_ -= k; | |
return Math.sqrt(1 + y_ * y_); | |
}; | |
} | |
function gingeryProjection() { | |
var n = 6, ρ = 30 * radians, cρ = Math.cos(ρ), sρ = Math.sin(ρ), m = projectionMutator(gingery), p = m(ρ, n), stream_ = p.stream, ε = .01, cr = -Math.cos(ε * radians), sr = Math.sin(ε * radians); | |
p.radius = function(_) { | |
if (!arguments.length) return ρ * degrees; | |
cρ = Math.cos(ρ = _ * radians); | |
sρ = Math.sin(ρ); | |
return m(ρ, n); | |
}; | |
p.lobes = function(_) { | |
if (!arguments.length) return n; | |
return m(ρ, 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, δ = 2 * π / n, φ = 0; i < n; ++i, φ -= δ) { | |
sphereStream.point(Math.atan2(sr * Math.cos(φ), cr) * degrees, Math.asin(sr * Math.sin(φ)) * degrees); | |
sphereStream.point(Math.atan2(sρ * Math.cos(φ - δ / 2), cρ) * degrees, Math.asin(sρ * Math.sin(φ - δ / 2)) * degrees); | |
} | |
sphereStream.lineEnd(), sphereStream.polygonEnd(); | |
}; | |
return rotateStream; | |
}; | |
return p; | |
} | |
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 * .5 * h; | |
} | |
(d3.geo.gingery = gingeryProjection).raw = gingery; | |
function ginzburgPolyconic(a, b, c, d, e, f, g, h) { | |
if (arguments.length < 8) h = 0; | |
function forward(λ, φ) { | |
if (!φ) return [ a * λ / π, 0 ]; | |
var φ2 = φ * φ, xB = a + φ2 * (b + φ2 * (c + φ2 * d)), yB = φ * (e - 1 + φ2 * (f - h + φ2 * g)), m = (xB * xB + yB * yB) / (2 * yB), α = λ * Math.asin(xB / m) / π; | |
return [ m * Math.sin(α), φ * (1 + φ2 * h) + m * (1 - Math.cos(α)) ]; | |
} | |
forward.invert = function(x, y) { | |
var λ = π * x / a, φ = y, δλ, δφ, i = 50; | |
do { | |
var φ2 = φ * φ, xB = a + φ2 * (b + φ2 * (c + φ2 * d)), yB = φ * (e - 1 + φ2 * (f - h + φ2 * g)), p = xB * xB + yB * yB, q = 2 * yB, m = p / q, m2 = m * m, dαdλ = Math.asin(xB / m) / π, α = λ * dαdλ; | |
xB2 = xB * xB, dxBdφ = (2 * b + φ2 * (4 * c + φ2 * 6 * d)) * φ, dyBdφ = e + φ2 * (3 * f + φ2 * 5 * g), | |
dpdφ = 2 * (xB * dxBdφ + yB * (dyBdφ - 1)), dqdφ = 2 * (dyBdφ - 1), dmdφ = (dpdφ * q - p * dqdφ) / (q * q), | |
cosα = Math.cos(α), sinα = Math.sin(α), mcosα = m * cosα, msinα = m * sinα, dαdφ = λ / π * (1 / asqrt(1 - xB2 / m2)) * (dxBdφ * m - xB * dmdφ) / m2, | |
fx = msinα - x, fy = φ * (1 + φ2 * h) + m - mcosα - y, δxδφ = dmdφ * sinα + mcosα * dαdφ, | |
δxδλ = mcosα * dαdλ, δyδφ = 1 + dmdφ - (dmdφ * cosα - msinα * dαdφ), δyδλ = msinα * dαdλ, | |
denominator = δxδφ * δyδλ - δyδφ * δxδλ; | |
if (!denominator) break; | |
λ -= δλ = (fy * δxδφ - fx * δyδφ) / denominator; | |
φ -= δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return [ λ, φ ]; | |
}; | |
return forward; | |
} | |
var ginzburg4 = ginzburgPolyconic(2.8284, -1.6988, .75432, -.18071, 1.76003, -.38914, .042555); | |
(d3.geo.ginzburg4 = function() { | |
return projection(ginzburg4); | |
}).raw = ginzburg4; | |
var ginzburg5 = ginzburgPolyconic(2.583819, -.835827, .170354, -.038094, 1.543313, -.411435, .082742); | |
(d3.geo.ginzburg5 = function() { | |
return projection(ginzburg5); | |
}).raw = ginzburg5; | |
var ginzburg6 = ginzburgPolyconic(5 / 6 * π, -.62636, -.0344, 0, 1.3493, -.05524, 0, .045); | |
(d3.geo.ginzburg6 = function() { | |
return projection(ginzburg6); | |
}).raw = ginzburg6; | |
function ginzburg8(λ, φ) { | |
var λ2 = λ * λ, φ2 = φ * φ; | |
return [ λ * (1 - .162388 * φ2) * (.87 - 952426e-9 * λ2 * λ2), φ * (1 + φ2 / 12) ]; | |
} | |
ginzburg8.invert = function(x, y) { | |
var λ = x, φ = y, i = 50, δ; | |
do { | |
var φ2 = φ * φ; | |
φ -= δ = (φ * (1 + φ2 / 12) - y) / (1 + φ2 / 4); | |
} while (Math.abs(δ) > ε && --i > 0); | |
i = 50; | |
x /= 1 - .162388 * φ2; | |
do { | |
var λ4 = (λ4 = λ * λ) * λ4; | |
λ -= δ = (λ * (.87 - 952426e-9 * λ4) - x) / (.87 - .00476213 * λ4); | |
} while (Math.abs(δ) > ε && --i > 0); | |
return [ λ, φ ]; | |
}; | |
(d3.geo.ginzburg8 = function() { | |
return projection(ginzburg8); | |
}).raw = ginzburg8; | |
var ginzburg9 = ginzburgPolyconic(2.6516, -.76534, .19123, -.047094, 1.36289, -.13965, .031762); | |
(d3.geo.ginzburg9 = function() { | |
return projection(ginzburg9); | |
}).raw = ginzburg9; | |
function quincuncialProjection(projectHemisphere) { | |
var dx = projectHemisphere(halfπ, 0)[0] - projectHemisphere(-halfπ, 0)[0]; | |
function projection() { | |
var quincuncial = false, m = projectionMutator(projectAt), p = m(quincuncial); | |
p.quincuncial = function(_) { | |
if (!arguments.length) return quincuncial; | |
return m(quincuncial = !!_); | |
}; | |
return p; | |
} | |
function projectAt(quincuncial) { | |
var forward = quincuncial ? function(λ, φ) { | |
var t = Math.abs(λ) < halfπ, p = projectHemisphere(t ? λ : λ > 0 ? λ - π : λ + π, φ); | |
var x = (p[0] - p[1]) * Math.SQRT1_2, y = (p[0] + p[1]) * Math.SQRT1_2; | |
if (t) return [ x, y ]; | |
var d = dx * Math.SQRT1_2, s = x > 0 ^ y > 0 ? -1 : 1; | |
return [ s * x - sgn(y) * d, s * y - sgn(x) * d ]; | |
} : function(λ, φ) { | |
var s = λ > 0 ? -.5 : .5, point = projectHemisphere(λ + s * π, φ); | |
point[0] -= s * dx; | |
return point; | |
}; | |
if (projectHemisphere.invert) forward.invert = quincuncial ? function(x0, y0) { | |
var x = (x0 + y0) * Math.SQRT1_2, y = (y0 - x0) * Math.SQRT1_2, t = Math.abs(x) < .5 * dx && Math.abs(y) < .5 * dx; | |
if (!t) { | |
var d = dx * Math.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) * Math.SQRT1_2; | |
y = (x1 - y1) * Math.SQRT1_2; | |
} | |
var p = projectHemisphere.invert(x, y); | |
if (!t) p[0] += x > 0 ? π : -π; | |
return p; | |
} : function(x, y) { | |
var s = x > 0 ? -.5 : .5, location = projectHemisphere.invert(x + s * dx, y), λ = location[0] - s * π; | |
if (λ < -π) λ += 2 * π; else if (λ > π) λ -= 2 * π; | |
location[0] = λ; | |
return location; | |
}; | |
return forward; | |
} | |
projection.raw = projectAt; | |
return projection; | |
} | |
function gringorten(λ, φ) { | |
var sλ = sgn(λ), sφ = sgn(φ), cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(sφ * φ); | |
λ = Math.abs(Math.atan2(y, z)); | |
φ = asin(x); | |
if (Math.abs(λ - halfπ) > ε) λ %= halfπ; | |
var point = gringortenHexadecant(λ > π / 4 ? halfπ - λ : λ, φ); | |
if (λ > π / 4) z = point[0], point[0] = -point[1], point[1] = -z; | |
return point[0] *= sλ, point[1] *= -sφ, point; | |
} | |
gringorten.invert = function(x, y) { | |
var sx = sgn(x), sy = sgn(y), x0 = -sx * x, y0 = -sy * y, t = y0 / x0 < 1, p = gringortenHexadecantInvert(t ? y0 : x0, t ? x0 : y0), λ = p[0], φ = p[1]; | |
if (t) λ = -halfπ - λ; | |
var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ); | |
return [ sx * (Math.atan2(y, -z) + π), sy * asin(x) ]; | |
}; | |
function gringortenHexadecant(λ, φ) { | |
if (φ === halfπ) return [ 0, 0 ]; | |
var sinφ = Math.sin(φ), r = sinφ * sinφ, r2 = r * r, j = 1 + r2, k = 1 + 3 * r2, q = 1 - r2, z = asin(1 / Math.sqrt(j)), v = q + r * j * z, p2 = (1 - sinφ) / v, p = Math.sqrt(p2), a2 = p2 * j, a = Math.sqrt(a2), h = p * q; | |
if (λ === 0) return [ 0, -(h + r * a) ]; | |
var cosφ = Math.cos(φ), secφ = 1 / cosφ, drdφ = 2 * sinφ * cosφ, dvdφ = (-3 * r + z * k) * drdφ, dp2dφ = (-v * cosφ - (1 - sinφ) * dvdφ) / (v * v), dpdφ = .5 * dp2dφ / p, dhdφ = q * dpdφ - 2 * r * p * drdφ, dra2dφ = r * j * dp2dφ + p2 * k * drdφ, μ = -secφ * drdφ, ν = -secφ * dra2dφ, ζ = -2 * secφ * dhdφ, Λ = 4 * λ / π; | |
if (λ > .222 * π || φ < π / 4 && λ > .175 * π) { | |
var x = (h + r * asqrt(a2 * (1 + r2) - h * h)) / (1 + r2); | |
if (λ > π / 4) return [ x, x ]; | |
var x1 = x, x0 = .5 * x, i = 50; | |
x = .5 * (x0 + x1); | |
do { | |
var g = Math.sqrt(a2 - x * x), f = x * (ζ + μ * g) + ν * asin(x / a) - Λ; | |
if (!f) break; | |
if (f < 0) x0 = x; else x1 = x; | |
x = .5 * (x0 + x1); | |
} while (Math.abs(x1 - x0) > ε && --i > 0); | |
} else { | |
var x = ε, i = 25, δ; | |
do { | |
var x2 = x * x, g = asqrt(a2 - x2), ζμg = ζ + μ * g, f = x * ζμg + ν * asin(x / a) - Λ, df = ζμg + (ν - μ * x2) / g; | |
x -= δ = g ? f / df : 0; | |
} while (Math.abs(δ) > ε && --i > 0); | |
} | |
return [ x, -h - r * asqrt(a2 - x * x) ]; | |
} | |
function gringortenHexadecantInvert(x, y) { | |
var x0 = 0, x1 = 1, r = .5, i = 50; | |
while (true) { | |
var r2 = r * r, sinφ = Math.sqrt(r), z = Math.asin(1 / Math.sqrt(1 + r2)), v = 1 - r2 + r * (1 + r2) * z, p2 = (1 - sinφ) / v, p = Math.sqrt(p2), a2 = p2 * (1 + r2), h = p * (1 - r2), g2 = a2 - x * x, g = Math.sqrt(g2), y0 = y + h + r * g; | |
if (Math.abs(x1 - x0) < ε2 || --i === 0 || y0 === 0) break; | |
if (y0 > 0) x0 = r; else x1 = r; | |
r = .5 * (x0 + x1); | |
} | |
if (!i) return null; | |
var φ = Math.asin(sinφ), cosφ = Math.cos(φ), secφ = 1 / cosφ, drdφ = 2 * sinφ * cosφ, dvdφ = (-3 * r + z * (1 + 3 * r2)) * drdφ, dp2dφ = (-v * cosφ - (1 - sinφ) * dvdφ) / (v * v), dpdφ = .5 * dp2dφ / p, dhdφ = (1 - r2) * dpdφ - 2 * r * p * drdφ, ζ = -2 * secφ * dhdφ, μ = -secφ * drdφ, ν = -secφ * (r * (1 + r2) * dp2dφ + p2 * (1 + 3 * r2) * drdφ); | |
return [ π / 4 * (x * (ζ + μ * g) + ν * Math.asin(x / Math.sqrt(a2))), φ ]; | |
} | |
d3.geo.gringorten = quincuncialProjection(gringorten); | |
function ellipticJi(u, v, m) { | |
if (!u) { | |
var b = ellipticJ(v, 1 - m); | |
return [ [ 0, b[0] / b[1] ], [ 1 / b[1], 0 ], [ b[2] / b[1], 0 ] ]; | |
} | |
var a = ellipticJ(u, m); | |
if (!v) return [ [ a[0], 0 ], [ a[1], 0 ], [ a[2], 0 ] ]; | |
var b = ellipticJ(v, 1 - m), denominator = b[1] * b[1] + m * a[0] * a[0] * b[0] * b[0]; | |
return [ [ a[0] * b[2] / denominator, a[1] * a[2] * b[0] * b[1] / denominator ], [ a[1] * b[1] / denominator, -a[0] * a[2] * b[0] * b[2] / denominator ], [ a[2] * b[1] * b[2] / denominator, -m * a[0] * a[1] * b[0] / denominator ] ]; | |
} | |
function ellipticJ(u, m) { | |
var ai, b, φ, t, twon; | |
if (m < ε) { | |
t = Math.sin(u); | |
b = Math.cos(u); | |
ai = .25 * m * (u - t * b); | |
return [ t - ai * b, b + ai * t, 1 - .5 * m * t * t, u - ai ]; | |
} | |
if (m >= 1 - ε) { | |
ai = .25 * (1 - m); | |
b = cosh(u); | |
t = tanh(u); | |
φ = 1 / b; | |
twon = b * sinh(u); | |
return [ t + ai * (twon - u) / (b * b), φ - ai * t * φ * (twon - u), φ + ai * t * φ * (twon + u), 2 * Math.atan(Math.exp(u)) - halfπ + ai * (twon - u) / b ]; | |
} | |
var a = [ 1, 0, 0, 0, 0, 0, 0, 0, 0 ], c = [ Math.sqrt(m), 0, 0, 0, 0, 0, 0, 0, 0 ], i = 0; | |
b = Math.sqrt(1 - m); | |
twon = 1; | |
while (Math.abs(c[i] / a[i]) > ε && i < 8) { | |
ai = a[i++]; | |
c[i] = .5 * (ai - b); | |
a[i] = .5 * (ai + b); | |
b = asqrt(ai * b); | |
twon *= 2; | |
} | |
φ = twon * a[i] * u; | |
do { | |
t = c[i] * Math.sin(b = φ) / a[i]; | |
φ = .5 * (asin(t) + φ); | |
} while (--i); | |
return [ Math.sin(φ), t = Math.cos(φ), t / Math.cos(φ - b), φ ]; | |
} | |
function ellipticFi(φ, ψ, m) { | |
var r = Math.abs(φ), i = Math.abs(ψ), sinhψ = sinh(i); | |
if (r) { | |
var cscφ = 1 / Math.sin(r), cotφ2 = 1 / (Math.tan(r) * Math.tan(r)), b = -(cotφ2 + m * sinhψ * sinhψ * cscφ * cscφ - 1 + m), c = (m - 1) * cotφ2, cotλ2 = .5 * (-b + Math.sqrt(b * b - 4 * c)); | |
return [ ellipticF(Math.atan(1 / Math.sqrt(cotλ2)), m) * sgn(φ), ellipticF(Math.atan(asqrt((cotλ2 / cotφ2 - 1) / m)), 1 - m) * sgn(ψ) ]; | |
} | |
return [ 0, ellipticF(Math.atan(sinhψ), 1 - m) * sgn(ψ) ]; | |
} | |
function ellipticF(φ, m) { | |
if (!m) return φ; | |
if (m === 1) return Math.log(Math.tan(φ / 2 + π / 4)); | |
var a = 1, b = Math.sqrt(1 - m), c = Math.sqrt(m); | |
for (var i = 0; Math.abs(c) > ε; i++) { | |
if (φ % π) { | |
var dφ = Math.atan(b * Math.tan(φ) / a); | |
if (dφ < 0) dφ += π; | |
φ += dφ + ~~(φ / π) * π; | |
} else φ += φ; | |
c = (a + b) / 2; | |
b = Math.sqrt(a * b); | |
c = ((a = c) - b) / 2; | |
} | |
return φ / (Math.pow(2, i) * a); | |
} | |
function guyou(λ, φ) { | |
var k_ = (Math.SQRT2 - 1) / (Math.SQRT2 + 1), k = Math.sqrt(1 - k_ * k_), K = ellipticF(halfπ, k * k), f = -1; | |
var ψ = Math.log(Math.tan(π / 4 + Math.abs(φ) / 2)), r = Math.exp(f * ψ) / Math.sqrt(k_), at = guyouComplexAtan(r * Math.cos(f * λ), r * Math.sin(f * λ)), t = ellipticFi(at[0], at[1], k * k); | |
return [ -t[1], (φ >= 0 ? 1 : -1) * (.5 * K - t[0]) ]; | |
} | |
function guyouComplexAtan(x, y) { | |
var x2 = x * x, y_1 = y + 1, t = 1 - x2 - y * y; | |
return [ .5 * ((x >= 0 ? halfπ : -halfπ) - Math.atan2(t, 2 * x)), -.25 * Math.log(t * t + 4 * x2) + .5 * Math.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 ]; | |
} | |
guyou.invert = function(x, y) { | |
var k_ = (Math.SQRT2 - 1) / (Math.SQRT2 + 1), k = Math.sqrt(1 - k_ * k_), K = ellipticF(halfπ, k * k), f = -1; | |
var j = ellipticJi(.5 * K - y, -x, k * k), tn = guyouComplexDivide(j[0], j[1]), λ = Math.atan2(tn[1], tn[0]) / f; | |
return [ λ, 2 * Math.atan(Math.exp(.5 / f * Math.log(k_ * tn[0] * tn[0] + k_ * tn[1] * tn[1]))) - halfπ ]; | |
}; | |
d3.geo.guyou = quincuncialProjection(guyou); | |
function hammerRetroazimuthal(φ0) { | |
var sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), rotate = hammerRetroazimuthalRotation(φ0); | |
rotate.invert = hammerRetroazimuthalRotation(-φ0); | |
function forward(λ, φ) { | |
var p = rotate(λ, φ); | |
λ = p[0], φ = p[1]; | |
var sinφ = Math.sin(φ), cosφ = Math.cos(φ), cosλ = Math.cos(λ), z = acos(sinφ0 * sinφ + cosφ0 * cosφ * cosλ), sinz = Math.sin(z), K = Math.abs(sinz) > ε ? z / sinz : 1; | |
return [ K * cosφ0 * Math.sin(λ), (Math.abs(λ) > halfπ ? K : -K) * (sinφ0 * cosφ - cosφ0 * sinφ * cosλ) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ = Math.sqrt(x * x + y * y), sinz = -Math.sin(ρ), cosz = Math.cos(ρ), a = ρ * cosz, b = -y * sinz, c = ρ * sinφ0, d = asqrt(a * a + b * b - c * c), φ = Math.atan2(a * c + b * d, b * c - a * d), λ = (ρ > halfπ ? -1 : 1) * Math.atan2(x * sinz, ρ * Math.cos(φ) * cosz + y * Math.sin(φ) * sinz); | |
return rotate.invert(λ, φ); | |
}; | |
return forward; | |
} | |
function hammerRetroazimuthalRotation(φ0) { | |
var sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0); | |
return function(λ, φ) { | |
var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ); | |
return [ Math.atan2(y, x * cosφ0 - z * sinφ0), asin(z * cosφ0 + x * sinφ0) ]; | |
}; | |
} | |
function hammerRetroazimuthalProjection() { | |
var φ0 = 0, m = projectionMutator(hammerRetroazimuthal), p = m(φ0), rotate_ = p.rotate, stream_ = p.stream, circle = d3.geo.circle(); | |
p.parallel = function(_) { | |
if (!arguments.length) return φ0 / π * 180; | |
var r = p.rotate(); | |
return m(φ0 = _ * π / 180).rotate(r); | |
}; | |
p.rotate = function(_) { | |
if (!arguments.length) return _ = rotate_.call(p), _[1] += φ0 / π * 180, _; | |
rotate_.call(p, [ _[0], _[1] - φ0 / π * 180 ]); | |
circle.origin([ -_[0], -_[1] ]); | |
return p; | |
}; | |
p.stream = function(stream) { | |
stream = stream_(stream); | |
stream.sphere = function() { | |
stream.polygonStart(); | |
var ε = .01, ring = circle.angle(90 - ε)().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.angle(90 + ε)().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; | |
} | |
(d3.geo.hammerRetroazimuthal = hammerRetroazimuthalProjection).raw = hammerRetroazimuthal; | |
var hammerAzimuthalEqualArea = d3.geo.azimuthalEqualArea.raw; | |
function hammer(A, B) { | |
if (arguments.length < 2) B = A; | |
if (B === 1) return hammerAzimuthalEqualArea; | |
if (B === Infinity) return hammerQuarticAuthalic; | |
function forward(λ, φ) { | |
var coordinates = hammerAzimuthalEqualArea(λ / B, φ); | |
coordinates[0] *= A; | |
return coordinates; | |
} | |
forward.invert = function(x, y) { | |
var coordinates = hammerAzimuthalEqualArea.invert(x / A, y); | |
coordinates[0] *= B; | |
return coordinates; | |
}; | |
return forward; | |
} | |
function hammerProjection() { | |
var B = 2, m = projectionMutator(hammer), p = m(B); | |
p.coefficient = function(_) { | |
if (!arguments.length) return B; | |
return m(B = +_); | |
}; | |
return p; | |
} | |
function hammerQuarticAuthalic(λ, φ) { | |
return [ λ * Math.cos(φ) / Math.cos(φ /= 2), 2 * Math.sin(φ) ]; | |
} | |
hammerQuarticAuthalic.invert = function(x, y) { | |
var φ = 2 * asin(y / 2); | |
return [ x * Math.cos(φ / 2) / Math.cos(φ), φ ]; | |
}; | |
(d3.geo.hammer = hammerProjection).raw = hammer; | |
function hatano(λ, φ) { | |
var c = Math.sin(φ) * (φ < 0 ? 2.43763 : 2.67595); | |
for (var i = 0, δ; i < 20; i++) { | |
φ -= δ = (φ + Math.sin(φ) - c) / (1 + Math.cos(φ)); | |
if (Math.abs(δ) < ε) break; | |
} | |
return [ .85 * λ * Math.cos(φ *= .5), Math.sin(φ) * (φ < 0 ? 1.93052 : 1.75859) ]; | |
} | |
hatano.invert = function(x, y) { | |
var θ = Math.abs(θ = y * (y < 0 ? .5179951515653813 : .5686373742600607)) > 1 - ε ? θ > 0 ? halfπ : -halfπ : asin(θ); | |
return [ 1.1764705882352942 * x / Math.cos(θ), Math.abs(θ = ((θ += θ) + Math.sin(θ)) * (y < 0 ? .4102345310814193 : .3736990601468637)) > 1 - ε ? θ > 0 ? halfπ : -halfπ : asin(θ) ]; | |
}; | |
(d3.geo.hatano = function() { | |
return projection(hatano); | |
}).raw = hatano; | |
var healpixParallel = 41 + 48 / 36 + 37 / 3600; | |
function healpix(h) { | |
var lambert = d3.geo.cylindricalEqualArea.raw(0), φ0 = healpixParallel * π / 180, dx0 = 2 * π, dx1 = d3.geo.collignon.raw(π, φ0)[0] - d3.geo.collignon.raw(-π, φ0)[0], y0 = lambert(0, φ0)[1], y1 = d3.geo.collignon.raw(0, φ0)[1], dy1 = d3.geo.collignon.raw(0, halfπ)[1] - y1, k = 2 * π / h; | |
function forward(λ, φ) { | |
var point, φ2 = Math.abs(φ); | |
if (φ2 > φ0) { | |
var i = Math.min(h - 1, Math.max(0, Math.floor((λ + π) / k))); | |
λ += π * (h - 1) / h - i * k; | |
point = d3.geo.collignon.raw(λ, φ2); | |
point[0] = point[0] * dx0 / dx1 - dx0 * (h - 1) / (2 * h) + i * dx0 / h; | |
point[1] = y0 + (point[1] - y1) * 4 * dy1 / dx0; | |
if (φ < 0) point[1] = -point[1]; | |
} else { | |
point = lambert(λ, φ); | |
} | |
point[0] /= 2; | |
return point; | |
} | |
forward.invert = function(x, y) { | |
x *= 2; | |
var y2 = Math.abs(y); | |
if (y2 > y0) { | |
var i = Math.min(h - 1, Math.max(0, Math.floor((x + π) / k))); | |
x = (x + π * (h - 1) / h - i * k) * dx1 / dx0; | |
var point = d3.geo.collignon.raw.invert(x, .25 * (y2 - y0) * dx0 / dy1 + y1); | |
point[0] -= π * (h - 1) / h - i * k; | |
if (y < 0) point[1] = -point[1]; | |
return point; | |
} | |
return lambert.invert(x, y); | |
}; | |
return forward; | |
} | |
function healpixProjection() { | |
var n = 2, m = projectionMutator(healpix), p = m(n), stream_ = p.stream; | |
p.lobes = function(_) { | |
if (!arguments.length) return n; | |
return m(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() { | |
d3.geo.stream(sphere(), sphereStream); | |
}; | |
return rotateStream; | |
}; | |
function sphere() { | |
var step = 180 / n; | |
return { | |
type: "Polygon", | |
coordinates: [ d3.range(-180, 180 + step / 2, step).map(function(x, i) { | |
return [ x, i & 1 ? 90 - 1e-6 : healpixParallel ]; | |
}).concat(d3.range(180, -180 - step / 2, -step).map(function(x, i) { | |
return [ x, i & 1 ? -90 + 1e-6 : -healpixParallel ]; | |
})) ] | |
}; | |
} | |
return p; | |
} | |
(d3.geo.healpix = healpixProjection).raw = healpix; | |
function hill(K) { | |
var L = 1 + K, sinβ = Math.sin(1 / L), β = asin(sinβ), A = 2 * Math.sqrt(π / (B = π + 4 * β * L)), B, ρ0 = .5 * A * (L + Math.sqrt(K * (2 + K))), K2 = K * K, L2 = L * L; | |
function forward(λ, φ) { | |
var t = 1 - Math.sin(φ), ρ, ω; | |
if (t && t < 2) { | |
var θ = halfπ - φ, i = 25, δ; | |
do { | |
var sinθ = Math.sin(θ), cosθ = Math.cos(θ), β_β1 = β + Math.atan2(sinθ, L - cosθ), C = 1 + L2 - 2 * L * cosθ; | |
θ -= δ = (θ - K2 * β - L * sinθ + C * β_β1 - .5 * t * B) / (2 * L * sinθ * β_β1); | |
} while (Math.abs(δ) > ε2 && --i > 0); | |
ρ = A * Math.sqrt(C); | |
ω = λ * β_β1 / π; | |
} else { | |
ρ = A * (K + t); | |
ω = λ * β / π; | |
} | |
return [ ρ * Math.sin(ω), ρ0 - ρ * Math.cos(ω) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ2 = x * x + (y -= ρ0) * y, cosθ = (1 + L2 - ρ2 / (A * A)) / (2 * L), θ = acos(cosθ), sinθ = Math.sin(θ), β_β1 = β + Math.atan2(sinθ, L - cosθ); | |
return [ asin(x / Math.sqrt(ρ2)) * π / β_β1, asin(1 - 2 * (θ - K2 * β - L * sinθ + (1 + L2 - 2 * L * cosθ) * β_β1) / B) ]; | |
}; | |
return forward; | |
} | |
function hillProjection() { | |
var K = 1, m = projectionMutator(hill), p = m(K); | |
p.ratio = function(_) { | |
if (!arguments.length) return K; | |
return m(K = +_); | |
}; | |
return p; | |
} | |
(d3.geo.hill = hillProjection).raw = hill; | |
var sinuMollweideφ = .7109889596207567, sinuMollweideY = .0528035274542; | |
function sinuMollweide(λ, φ) { | |
return φ > -sinuMollweideφ ? (λ = mollweide(λ, φ), λ[1] += sinuMollweideY, λ) : sinusoidal(λ, φ); | |
} | |
sinuMollweide.invert = function(x, y) { | |
return y > -sinuMollweideφ ? mollweide.invert(x, y - sinuMollweideY) : sinusoidal.invert(x, y); | |
}; | |
(d3.geo.sinuMollweide = function() { | |
return projection(sinuMollweide).rotate([ -20, -55 ]); | |
}).raw = sinuMollweide; | |
function homolosine(λ, φ) { | |
return Math.abs(φ) > sinuMollweideφ ? (λ = mollweide(λ, φ), λ[1] -= φ > 0 ? sinuMollweideY : -sinuMollweideY, | |
λ) : sinusoidal(λ, φ); | |
} | |
homolosine.invert = function(x, y) { | |
return Math.abs(y) > sinuMollweideφ ? mollweide.invert(x, y + (y > 0 ? sinuMollweideY : -sinuMollweideY)) : sinusoidal.invert(x, y); | |
}; | |
(d3.geo.homolosine = function() { | |
return projection(homolosine); | |
}).raw = homolosine; | |
function kavrayskiy7(λ, φ) { | |
return [ 3 * λ / (2 * π) * Math.sqrt(π * π / 3 - φ * φ), φ ]; | |
} | |
kavrayskiy7.invert = function(x, y) { | |
return [ 2 / 3 * π * x / Math.sqrt(π * π / 3 - y * y), y ]; | |
}; | |
(d3.geo.kavrayskiy7 = function() { | |
return projection(kavrayskiy7); | |
}).raw = kavrayskiy7; | |
function lagrange(n) { | |
function forward(λ, φ) { | |
if (Math.abs(Math.abs(φ) - halfπ) < ε) return [ 0, φ < 0 ? -2 : 2 ]; | |
var sinφ = Math.sin(φ), v = Math.pow((1 + sinφ) / (1 - sinφ), n / 2), c = .5 * (v + 1 / v) + Math.cos(λ *= n); | |
return [ 2 * Math.sin(λ) / c, (v - 1 / v) / c ]; | |
} | |
forward.invert = function(x, y) { | |
var y0 = Math.abs(y); | |
if (Math.abs(y0 - 2) < ε) return x ? null : [ 0, sgn(y) * halfπ ]; | |
if (y0 > 2) return null; | |
x /= 2, y /= 2; | |
var x2 = x * x, y2 = y * y, t = 2 * y / (1 + x2 + y2); | |
t = Math.pow((1 + t) / (1 - t), 1 / n); | |
return [ Math.atan2(2 * x, 1 - x2 - y2) / n, asin((t - 1) / (t + 1)) ]; | |
}; | |
return forward; | |
} | |
function lagrangeProjection() { | |
var n = .5, m = projectionMutator(lagrange), p = m(n); | |
p.spacing = function(_) { | |
if (!arguments.length) return n; | |
return m(n = +_); | |
}; | |
return p; | |
} | |
(d3.geo.lagrange = lagrangeProjection).raw = lagrange; | |
function larrivee(λ, φ) { | |
return [ λ * (1 + Math.sqrt(Math.cos(φ))) / 2, φ / (Math.cos(φ / 2) * Math.cos(λ / 6)) ]; | |
} | |
larrivee.invert = function(x, y) { | |
var x0 = Math.abs(x), y0 = Math.abs(y), π_sqrt2 = π / Math.SQRT2, λ = ε, φ = halfπ; | |
if (y0 < π_sqrt2) φ *= y0 / π_sqrt2; else λ += 6 * acos(π_sqrt2 / y0); | |
for (var i = 0; i < 25; i++) { | |
var sinφ = Math.sin(φ), sqrtcosφ = asqrt(Math.cos(φ)), sinφ_2 = Math.sin(φ / 2), cosφ_2 = Math.cos(φ / 2), sinλ_6 = Math.sin(λ / 6), cosλ_6 = Math.cos(λ / 6), f0 = .5 * λ * (1 + sqrtcosφ) - x0, f1 = φ / (cosφ_2 * cosλ_6) - y0, df0dφ = sqrtcosφ ? -.25 * λ * sinφ / sqrtcosφ : 0, df0dλ = .5 * (1 + sqrtcosφ), df1dφ = (1 + .5 * φ * sinφ_2 / cosφ_2) / (cosφ_2 * cosλ_6), df1dλ = φ / cosφ_2 * (sinλ_6 / 6) / (cosλ_6 * cosλ_6), denom = df0dφ * df1dλ - df1dφ * df0dλ, dφ = (f0 * df1dλ - f1 * df0dλ) / denom, dλ = (f1 * df0dφ - f0 * df1dφ) / denom; | |
φ -= dφ; | |
λ -= dλ; | |
if (Math.abs(dφ) < ε && Math.abs(dλ) < ε) break; | |
} | |
return [ x < 0 ? -λ : λ, y < 0 ? -φ : φ ]; | |
}; | |
(d3.geo.larrivee = function() { | |
return projection(larrivee); | |
}).raw = larrivee; | |
function laskowski(λ, φ) { | |
var λ2 = λ * λ, φ2 = φ * φ; | |
return [ λ * (.975534 + φ2 * (-.119161 + λ2 * -.0143059 + φ2 * -.0547009)), φ * (1.00384 + λ2 * (.0802894 + φ2 * -.02855 + λ2 * 199025e-9) + φ2 * (.0998909 + φ2 * -.0491032)) ]; | |
} | |
laskowski.invert = function(x, y) { | |
var λ = sgn(x) * π, φ = y / 2, i = 50; | |
do { | |
var λ2 = λ * λ, φ2 = φ * φ, λφ = λ * φ, fx = λ * (.975534 + φ2 * (-.119161 + λ2 * -.0143059 + φ2 * -.0547009)) - x, fy = φ * (1.00384 + λ2 * (.0802894 + φ2 * -.02855 + λ2 * 199025e-9) + φ2 * (.0998909 + φ2 * -.0491032)) - y, δxδλ = .975534 - φ2 * (.119161 + 3 * λ2 * .0143059 + φ2 * .0547009), δxδφ = -λφ * (2 * .119161 + 4 * .0547009 * φ2 + 2 * .0143059 * λ2), δyδλ = λφ * (2 * .0802894 + 4 * 199025e-9 * λ2 + 2 * -.02855 * φ2), δyδφ = 1.00384 + λ2 * (.0802894 + 199025e-9 * λ2) + φ2 * (3 * (.0998909 - .02855 * λ2) - 5 * .0491032 * φ2), denominator = δxδφ * δyδλ - δyδφ * δxδλ, δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
λ -= δλ, φ -= δφ; | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return i && [ λ, φ ]; | |
}; | |
(d3.geo.laskowski = function() { | |
return projection(laskowski); | |
}).raw = laskowski; | |
function littrow(λ, φ) { | |
return [ Math.sin(λ) / Math.cos(φ), Math.tan(φ) * Math.cos(λ) ]; | |
} | |
littrow.invert = function(x, y) { | |
var x2 = x * x, y2 = y * y, y2_1 = y2 + 1, cosφ = x ? Math.SQRT1_2 * Math.sqrt((y2_1 - Math.sqrt(x2 * x2 + 2 * x2 * (y2 - 1) + y2_1 * y2_1)) / x2 + 1) : 1 / Math.sqrt(y2_1); | |
return [ asin(x * cosφ), sgn(y) * acos(cosφ) ]; | |
}; | |
(d3.geo.littrow = function() { | |
return projection(littrow); | |
}).raw = littrow; | |
function loximuthal(φ0) { | |
var cosφ0 = Math.cos(φ0), tanφ0 = Math.tan(π / 4 + φ0 / 2); | |
function forward(λ, φ) { | |
var y = φ - φ0, x = Math.abs(y) < ε ? λ * cosφ0 : Math.abs(x = π / 4 + φ / 2) < ε || Math.abs(Math.abs(x) - halfπ) < ε ? 0 : λ * y / Math.log(Math.tan(x) / tanφ0); | |
return [ x, y ]; | |
} | |
forward.invert = function(x, y) { | |
var λ, φ = y + φ0; | |
return [ Math.abs(y) < ε ? x / cosφ0 : Math.abs(λ = π / 4 + φ / 2) < ε || Math.abs(Math.abs(λ) - halfπ) < ε ? 0 : x * Math.log(Math.tan(λ) / tanφ0) / y, φ ]; | |
}; | |
return forward; | |
} | |
(d3.geo.loximuthal = function() { | |
return parallel1Projection(loximuthal).parallel(40); | |
}).raw = loximuthal; | |
function miller(λ, φ) { | |
return [ λ, 1.25 * Math.log(Math.tan(π / 4 + .4 * φ)) ]; | |
} | |
miller.invert = function(x, y) { | |
return [ x, 2.5 * Math.atan(Math.exp(.8 * y)) - .625 * π ]; | |
}; | |
(d3.geo.miller = function() { | |
return projection(miller); | |
}).raw = miller; | |
function modifiedStereographic(C) { | |
var m = C.length - 1; | |
function forward(λ, φ) { | |
var cosφ = Math.cos(φ), k = 2 / (1 + cosφ * Math.cos(λ)), zr = k * cosφ * Math.sin(λ), zi = k * Math.sin(φ), 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, δr, δi; | |
zr -= δr = (ar * br + ai * bi) / denominator; | |
zi -= δi = (ai * br - ar * bi) / denominator; | |
} while (Math.abs(δr) + Math.abs(δi) > ε * ε && --i > 0); | |
if (i) { | |
var ρ = Math.sqrt(zr * zr + zi * zi), c = 2 * Math.atan(ρ * .5), sinc = Math.sin(c); | |
return [ Math.atan2(zr * sinc, ρ * Math.cos(c)), ρ ? asin(zi * sinc / ρ) : 0 ]; | |
} | |
}; | |
return forward; | |
} | |
var modifiedStereographicCoefficients = { | |
alaska: [ [ .9972523, 0 ], [ .0052513, -.0041175 ], [ .0074606, .0048125 ], [ -.0153783, -.1968253 ], [ .0636871, -.1408027 ], [ .3660976, -.2937382 ] ], | |
gs48: [ [ .98879, 0 ], [ 0, 0 ], [ -.050909, 0 ], [ 0, 0 ], [ .075528, 0 ] ], | |
gs50: [ [ .984299, 0 ], [ .0211642, .0037608 ], [ -.1036018, -.0575102 ], [ -.0329095, -.0320119 ], [ .0499471, .1223335 ], [ .026046, .0899805 ], [ 7388e-7, -.1435792 ], [ .0075848, -.1334108 ], [ -.0216473, .0776645 ], [ -.0225161, .0853673 ] ], | |
miller: [ [ .9245, 0 ], [ 0, 0 ], [ .01943, 0 ] ], | |
lee: [ [ .721316, 0 ], [ 0, 0 ], [ -.00881625, -.00617325 ] ] | |
}; | |
function modifiedStereographicProjection() { | |
var coefficients = modifiedStereographicCoefficients.miller, m = projectionMutator(modifiedStereographic), p = m(coefficients); | |
p.coefficients = function(_) { | |
if (!arguments.length) return coefficients; | |
return m(coefficients = typeof _ === "string" ? modifiedStereographicCoefficients[_] : _); | |
}; | |
return p; | |
} | |
(d3.geo.modifiedStereographic = modifiedStereographicProjection).raw = modifiedStereographic; | |
function mtFlatPolarParabolic(λ, φ) { | |
var sqrt6 = Math.sqrt(6), sqrt7 = Math.sqrt(7), θ = Math.asin(7 * Math.sin(φ) / (3 * sqrt6)); | |
return [ sqrt6 * λ * (2 * Math.cos(2 * θ / 3) - 1) / sqrt7, 9 * Math.sin(θ / 3) / sqrt7 ]; | |
} | |
mtFlatPolarParabolic.invert = function(x, y) { | |
var sqrt6 = Math.sqrt(6), sqrt7 = Math.sqrt(7), θ = 3 * asin(y * sqrt7 / 9); | |
return [ x * sqrt7 / (sqrt6 * (2 * Math.cos(2 * θ / 3) - 1)), asin(Math.sin(θ) * 3 * sqrt6 / 7) ]; | |
}; | |
(d3.geo.mtFlatPolarParabolic = function() { | |
return projection(mtFlatPolarParabolic); | |
}).raw = mtFlatPolarParabolic; | |
function mtFlatPolarQuartic(λ, φ) { | |
var k = (1 + Math.SQRT1_2) * Math.sin(φ), θ = φ; | |
for (var i = 0, δ; i < 25; i++) { | |
θ -= δ = (Math.sin(θ / 2) + Math.sin(θ) - k) / (.5 * Math.cos(θ / 2) + Math.cos(θ)); | |
if (Math.abs(δ) < ε) break; | |
} | |
return [ λ * (1 + 2 * Math.cos(θ) / Math.cos(θ / 2)) / (3 * Math.SQRT2), 2 * Math.sqrt(3) * Math.sin(θ / 2) / Math.sqrt(2 + Math.SQRT2) ]; | |
} | |
mtFlatPolarQuartic.invert = function(x, y) { | |
var sinθ_2 = y * Math.sqrt(2 + Math.SQRT2) / (2 * Math.sqrt(3)), θ = 2 * asin(sinθ_2); | |
return [ 3 * Math.SQRT2 * x / (1 + 2 * Math.cos(θ) / Math.cos(θ / 2)), asin((sinθ_2 + Math.sin(θ)) / (1 + Math.SQRT1_2)) ]; | |
}; | |
(d3.geo.mtFlatPolarQuartic = function() { | |
return projection(mtFlatPolarQuartic); | |
}).raw = mtFlatPolarQuartic; | |
function mtFlatPolarSinusoidal(λ, φ) { | |
var A = Math.sqrt(6 / (4 + π)), k = (1 + π / 4) * Math.sin(φ), θ = φ / 2; | |
for (var i = 0, δ; i < 25; i++) { | |
θ -= δ = (θ / 2 + Math.sin(θ) - k) / (.5 + Math.cos(θ)); | |
if (Math.abs(δ) < ε) break; | |
} | |
return [ A * (.5 + Math.cos(θ)) * λ / 1.5, A * θ ]; | |
} | |
mtFlatPolarSinusoidal.invert = function(x, y) { | |
var A = Math.sqrt(6 / (4 + π)), θ = y / A; | |
if (Math.abs(Math.abs(θ) - halfπ) < ε) θ = θ < 0 ? -halfπ : halfπ; | |
return [ 1.5 * x / (A * (.5 + Math.cos(θ))), asin((θ / 2 + Math.sin(θ)) / (1 + π / 4)) ]; | |
}; | |
(d3.geo.mtFlatPolarSinusoidal = function() { | |
return projection(mtFlatPolarSinusoidal); | |
}).raw = mtFlatPolarSinusoidal; | |
function naturalEarth(λ, φ) { | |
var φ2 = φ * φ, φ4 = φ2 * φ2; | |
return [ λ * (.8707 - .131979 * φ2 + φ4 * (-.013791 + φ4 * (.003971 * φ2 - .001529 * φ4))), φ * (1.007226 + φ2 * (.015085 + φ4 * (-.044475 + .028874 * φ2 - .005916 * φ4))) ]; | |
} | |
naturalEarth.invert = function(x, y) { | |
var φ = y, i = 25, δ; | |
do { | |
var φ2 = φ * φ, φ4 = φ2 * φ2; | |
φ -= δ = (φ * (1.007226 + φ2 * (.015085 + φ4 * (-.044475 + .028874 * φ2 - .005916 * φ4))) - y) / (1.007226 + φ2 * (.015085 * 3 + φ4 * (-.044475 * 7 + .028874 * 9 * φ2 - .005916 * 11 * φ4))); | |
} while (Math.abs(δ) > ε && --i > 0); | |
return [ x / (.8707 + (φ2 = φ * φ) * (-.131979 + φ2 * (-.013791 + φ2 * φ2 * φ2 * (.003971 - .001529 * φ2)))), φ ]; | |
}; | |
(d3.geo.naturalEarth = function() { | |
return projection(naturalEarth); | |
}).raw = naturalEarth; | |
function nellHammer(λ, φ) { | |
return [ λ * (1 + Math.cos(φ)) / 2, 2 * (φ - Math.tan(φ / 2)) ]; | |
} | |
nellHammer.invert = function(x, y) { | |
var p = y / 2; | |
for (var i = 0, δ = Infinity; i < 10 && Math.abs(δ) > ε; i++) { | |
var c = Math.cos(y / 2); | |
y -= δ = (y - Math.tan(y / 2) - p) / (1 - .5 / (c * c)); | |
} | |
return [ 2 * x / (1 + Math.cos(y)), y ]; | |
}; | |
(d3.geo.nellHammer = function() { | |
return projection(nellHammer); | |
}).raw = nellHammer; | |
var pattersonK1 = 1.0148, pattersonK2 = .23185, pattersonK3 = -.14499, pattersonK4 = .02406, pattersonC1 = pattersonK1, pattersonC2 = 5 * pattersonK2, pattersonC3 = 7 * pattersonK3, pattersonC4 = 9 * pattersonK4, pattersonYmax = 1.790857183; | |
function patterson(λ, φ) { | |
var φ2 = φ * φ; | |
return [ λ, φ * (pattersonK1 + φ2 * φ2 * (pattersonK2 + φ2 * (pattersonK3 + pattersonK4 * φ2))) ]; | |
} | |
patterson.invert = function(x, y) { | |
if (y > pattersonYmax) y = pattersonYmax; else if (y < -pattersonYmax) y = -pattersonYmax; | |
var yc = y, δ; | |
do { | |
var y2 = yc * yc; | |
yc -= δ = (yc * (pattersonK1 + y2 * y2 * (pattersonK2 + y2 * (pattersonK3 + pattersonK4 * y2))) - y) / (pattersonC1 + y2 * y2 * (pattersonC2 + y2 * (pattersonC3 + pattersonC4 * y2))); | |
} while (Math.abs(δ) > ε); | |
return [ x, yc ]; | |
}; | |
(d3.geo.patterson = function() { | |
return projection(patterson); | |
}).raw = patterson; | |
var peirceQuincuncialProjection = quincuncialProjection(guyou); | |
(d3.geo.peirceQuincuncial = function() { | |
return peirceQuincuncialProjection().quincuncial(true).rotate([ -90, -90, 45 ]).clipAngle(180 - 1e-6); | |
}).raw = peirceQuincuncialProjection.raw; | |
function polyconic(λ, φ) { | |
if (Math.abs(φ) < ε) return [ λ, 0 ]; | |
var tanφ = Math.tan(φ), k = λ * Math.sin(φ); | |
return [ Math.sin(k) / tanφ, φ + (1 - Math.cos(k)) / tanφ ]; | |
} | |
polyconic.invert = function(x, y) { | |
if (Math.abs(y) < ε) return [ x, 0 ]; | |
var k = x * x + y * y, φ = y * .5, i = 10, δ; | |
do { | |
var tanφ = Math.tan(φ), secφ = 1 / Math.cos(φ), j = k - 2 * y * φ + φ * φ; | |
φ -= δ = (tanφ * j + 2 * (φ - y)) / (2 + j * secφ * secφ + 2 * (φ - y) * tanφ); | |
} while (Math.abs(δ) > ε && --i > 0); | |
tanφ = Math.tan(φ); | |
return [ (Math.abs(y) < Math.abs(φ + 1 / tanφ) ? asin(x * tanφ) : sgn(x) * (acos(Math.abs(x * tanφ)) + halfπ)) / Math.sin(φ), φ ]; | |
}; | |
(d3.geo.polyconic = function() { | |
return projection(polyconic); | |
}).raw = polyconic; | |
function rectangularPolyconic(φ0) { | |
var sinφ0 = Math.sin(φ0); | |
function forward(λ, φ) { | |
var A = sinφ0 ? Math.tan(λ * sinφ0 / 2) / sinφ0 : λ / 2; | |
if (!φ) return [ 2 * A, -φ0 ]; | |
var E = 2 * Math.atan(A * Math.sin(φ)), cotφ = 1 / Math.tan(φ); | |
return [ Math.sin(E) * cotφ, φ + (1 - Math.cos(E)) * cotφ - φ0 ]; | |
} | |
forward.invert = function(x, y) { | |
if (Math.abs(y += φ0) < ε) return [ sinφ0 ? 2 * Math.atan(sinφ0 * x / 2) / sinφ0 : x, 0 ]; | |
var k = x * x + y * y, φ = 0, i = 10, δ; | |
do { | |
var tanφ = Math.tan(φ), secφ = 1 / Math.cos(φ), j = k - 2 * y * φ + φ * φ; | |
φ -= δ = (tanφ * j + 2 * (φ - y)) / (2 + j * secφ * secφ + 2 * (φ - y) * tanφ); | |
} while (Math.abs(δ) > ε && --i > 0); | |
var E = x * (tanφ = Math.tan(φ)), A = Math.tan(Math.abs(y) < Math.abs(φ + 1 / tanφ) ? asin(E) * .5 : acos(E) * .5 + π / 4) / Math.sin(φ); | |
return [ sinφ0 ? 2 * Math.atan(sinφ0 * A) / sinφ0 : 2 * A, φ ]; | |
}; | |
return forward; | |
} | |
(d3.geo.rectangularPolyconic = function() { | |
return parallel1Projection(rectangularPolyconic); | |
}).raw = rectangularPolyconic; | |
var robinsonConstants = [ [ .9986, -.062 ], [ 1, 0 ], [ .9986, .062 ], [ .9954, .124 ], [ .99, .186 ], [ .9822, .248 ], [ .973, .31 ], [ .96, .372 ], [ .9427, .434 ], [ .9216, .4958 ], [ .8962, .5571 ], [ .8679, .6176 ], [ .835, .6769 ], [ .7986, .7346 ], [ .7597, .7903 ], [ .7186, .8435 ], [ .6732, .8936 ], [ .6213, .9394 ], [ .5722, .9761 ], [ .5322, 1 ] ]; | |
robinsonConstants.forEach(function(d) { | |
d[1] *= 1.0144; | |
}); | |
function robinson(λ, φ) { | |
var i = Math.min(18, Math.abs(φ) * 36 / π), i0 = Math.floor(i), di = i - i0, ax = (k = robinsonConstants[i0])[0], ay = k[1], bx = (k = robinsonConstants[++i0])[0], by = k[1], cx = (k = robinsonConstants[Math.min(19, ++i0)])[0], cy = k[1], k; | |
return [ λ * (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2), (φ > 0 ? halfπ : -halfπ) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2) ]; | |
} | |
robinson.invert = function(x, y) { | |
var yy = y / halfπ, φ = yy * 90, i = Math.min(18, Math.abs(φ / 5)), i0 = Math.max(0, Math.floor(i)); | |
do { | |
var ay = robinsonConstants[i0][1], by = robinsonConstants[i0 + 1][1], cy = robinsonConstants[Math.min(19, i0 + 2)][1], u = cy - ay, v = cy - 2 * by + ay, t = 2 * (Math.abs(yy) - by) / u, c = v / u, di = t * (1 - c * t * (1 - 2 * c * t)); | |
if (di >= 0 || i0 === 1) { | |
φ = (y >= 0 ? 5 : -5) * (di + i); | |
var j = 50, δ; | |
do { | |
i = Math.min(18, Math.abs(φ) / 5); | |
i0 = Math.floor(i); | |
di = i - i0; | |
ay = robinsonConstants[i0][1]; | |
by = robinsonConstants[i0 + 1][1]; | |
cy = robinsonConstants[Math.min(19, i0 + 2)][1]; | |
φ -= (δ = (y >= 0 ? halfπ : -halfπ) * (by + di * (cy - ay) / 2 + di * di * (cy - 2 * by + ay) / 2) - y) * degrees; | |
} while (Math.abs(δ) > ε2 && --j > 0); | |
break; | |
} | |
} while (--i0 >= 0); | |
var ax = robinsonConstants[i0][0], bx = robinsonConstants[i0 + 1][0], cx = robinsonConstants[Math.min(19, i0 + 2)][0]; | |
return [ x / (bx + di * (cx - ax) / 2 + di * di * (cx - 2 * bx + ax) / 2), φ * radians ]; | |
}; | |
(d3.geo.robinson = function() { | |
return projection(robinson); | |
}).raw = robinson; | |
function satelliteVertical(P) { | |
function forward(λ, φ) { | |
var cosφ = Math.cos(φ), k = (P - 1) / (P - cosφ * Math.cos(λ)); | |
return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ2 = x * x + y * y, ρ = Math.sqrt(ρ2), sinc = (P - Math.sqrt(1 - ρ2 * (P + 1) / (P - 1))) / ((P - 1) / ρ + ρ / (P - 1)); | |
return [ Math.atan2(x * sinc, ρ * Math.sqrt(1 - sinc * sinc)), ρ ? asin(y * sinc / ρ) : 0 ]; | |
}; | |
return forward; | |
} | |
function satellite(P, ω) { | |
var vertical = satelliteVertical(P); | |
if (!ω) return vertical; | |
var cosω = Math.cos(ω), sinω = Math.sin(ω); | |
function forward(λ, φ) { | |
var coordinates = vertical(λ, φ), y = coordinates[1], A = y * sinω / (P - 1) + cosω; | |
return [ coordinates[0] * cosω / A, y / A ]; | |
} | |
forward.invert = function(x, y) { | |
var k = (P - 1) / (P - 1 - y * sinω); | |
return vertical.invert(k * x, k * y * cosω); | |
}; | |
return forward; | |
} | |
function satelliteProjection() { | |
var P = 1.4, ω = 0, m = projectionMutator(satellite), p = m(P, ω); | |
p.distance = function(_) { | |
if (!arguments.length) return P; | |
return m(P = +_, ω); | |
}; | |
p.tilt = function(_) { | |
if (!arguments.length) return ω * 180 / π; | |
return m(P, ω = _ * π / 180); | |
}; | |
return p; | |
} | |
(d3.geo.satellite = satelliteProjection).raw = satellite; | |
function times(λ, φ) { | |
var t = Math.tan(φ / 2), s = Math.sin(π / 4 * t); | |
return [ λ * (.74482 - .34588 * s * s), 1.70711 * t ]; | |
} | |
times.invert = function(x, y) { | |
var t = y / 1.70711, s = Math.sin(π / 4 * t); | |
return [ x / (.74482 - .34588 * s * s), 2 * Math.atan(t) ]; | |
}; | |
(d3.geo.times = function() { | |
return projection(times); | |
}).raw = times; | |
function twoPointEquidistant(z0) { | |
if (!z0) return d3.geo.azimuthalEquidistant.raw; | |
var λa = -z0 / 2, λb = -λa, z02 = z0 * z0, tanλ0 = Math.tan(λb), S = .5 / Math.sin(λb); | |
function forward(λ, φ) { | |
var za = acos(Math.cos(φ) * Math.cos(λ - λa)), zb = acos(Math.cos(φ) * Math.cos(λ - λb)), ys = φ < 0 ? -1 : 1; | |
za *= za, zb *= zb; | |
return [ (za - zb) / (2 * z0), ys * asqrt(4 * z02 * zb - (z02 - za + zb) * (z02 - za + zb)) / (2 * z0) ]; | |
} | |
forward.invert = function(x, y) { | |
var y2 = y * y, cosza = Math.cos(Math.sqrt(y2 + (t = x + λa) * t)), coszb = Math.cos(Math.sqrt(y2 + (t = x + λb) * t)), t, d; | |
return [ Math.atan2(d = cosza - coszb, t = (cosza + coszb) * tanλ0), (y < 0 ? -1 : 1) * acos(Math.sqrt(t * t + d * d) * S) ]; | |
}; | |
return forward; | |
} | |
function twoPointEquidistantProjection() { | |
var points = [ [ 0, 0 ], [ 0, 0 ] ], m = projectionMutator(twoPointEquidistant), p = m(0), rotate = p.rotate; | |
delete p.rotate; | |
p.points = function(_) { | |
if (!arguments.length) return points; | |
points = _; | |
var interpolate = d3.geo.interpolate(_[0], _[1]), origin = interpolate(.5), p = d3.geo.rotation([ -origin[0], -origin[1] ])(_[0]), b = interpolate.distance * .5, γ = -asin(Math.sin(p[1] * radians) / Math.sin(b)); | |
if (p[0] > 0) γ = π - γ; | |
rotate.call(p, [ -origin[0], -origin[1], -γ * degrees ]); | |
return m(b * 2); | |
}; | |
return p; | |
} | |
(d3.geo.twoPointEquidistant = twoPointEquidistantProjection).raw = twoPointEquidistant; | |
function twoPointAzimuthal(d) { | |
var cosd = Math.cos(d); | |
function forward(λ, φ) { | |
var coordinates = d3.geo.gnomonic.raw(λ, φ); | |
coordinates[0] *= cosd; | |
return coordinates; | |
} | |
forward.invert = function(x, y) { | |
return d3.geo.gnomonic.raw.invert(x / cosd, y); | |
}; | |
return forward; | |
} | |
function twoPointAzimuthalProjection() { | |
var points = [ [ 0, 0 ], [ 0, 0 ] ], m = projectionMutator(twoPointAzimuthal), p = m(0), rotate = p.rotate; | |
delete p.rotate; | |
p.points = function(_) { | |
if (!arguments.length) return points; | |
points = _; | |
var interpolate = d3.geo.interpolate(_[0], _[1]), origin = interpolate(.5), p = d3.geo.rotation([ -origin[0], -origin[1] ])(_[0]), b = interpolate.distance * .5, γ = -asin(Math.sin(p[1] * radians) / Math.sin(b)); | |
if (p[0] > 0) γ = π - γ; | |
rotate.call(p, [ -origin[0], -origin[1], -γ * degrees ]); | |
return m(b); | |
}; | |
return p; | |
} | |
(d3.geo.twoPointAzimuthal = twoPointAzimuthalProjection).raw = twoPointAzimuthal; | |
function vanDerGrinten(λ, φ) { | |
if (Math.abs(φ) < ε) return [ λ, 0 ]; | |
var sinθ = Math.abs(φ / halfπ), θ = asin(sinθ); | |
if (Math.abs(λ) < ε || Math.abs(Math.abs(φ) - halfπ) < ε) return [ 0, sgn(φ) * π * Math.tan(θ / 2) ]; | |
var cosθ = Math.cos(θ), A = Math.abs(π / λ - λ / π) / 2, A2 = A * A, G = cosθ / (sinθ + cosθ - 1), P = G * (2 / sinθ - 1), P2 = P * P, P2_A2 = P2 + A2, G_P2 = G - P2, Q = A2 + G; | |
return [ sgn(λ) * π * (A * G_P2 + Math.sqrt(A2 * G_P2 * G_P2 - P2_A2 * (G * G - P2))) / P2_A2, sgn(φ) * π * (P * Q - A * Math.sqrt((A2 + 1) * P2_A2 - Q * Q)) / P2_A2 ]; | |
} | |
vanDerGrinten.invert = function(x, y) { | |
if (Math.abs(y) < ε) return [ x, 0 ]; | |
if (Math.abs(x) < ε) return [ 0, halfπ * Math.sin(2 * Math.atan(y / π)) ]; | |
var x2 = (x /= π) * x, y2 = (y /= π) * y, x2_y2 = x2 + y2, z = x2_y2 * x2_y2, c1 = -Math.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 * Math.sqrt(-a1 / 3), θ1 = acos(3 * d / (a1 * m1)) / 3; | |
return [ π * (x2_y2 - 1 + Math.sqrt(1 + 2 * (x2 - y2) + z)) / (2 * x), sgn(y) * π * (-m1 * Math.cos(θ1 + π / 3) - c2 / (3 * c3)) ]; | |
}; | |
(d3.geo.vanDerGrinten = function() { | |
return projection(vanDerGrinten); | |
}).raw = vanDerGrinten; | |
function vanDerGrinten2(λ, φ) { | |
if (Math.abs(φ) < ε) return [ λ, 0 ]; | |
var sinθ = Math.abs(φ / halfπ), θ = asin(sinθ); | |
if (Math.abs(λ) < ε || Math.abs(Math.abs(φ) - halfπ) < ε) return [ 0, sgn(φ) * π * Math.tan(θ / 2) ]; | |
var cosθ = Math.cos(θ), A = Math.abs(π / λ - λ / π) / 2, A2 = A * A, x1 = cosθ * (Math.sqrt(1 + A2) - A * cosθ) / (1 + A2 * sinθ * sinθ); | |
return [ sgn(λ) * π * x1, sgn(φ) * π * asqrt(1 - x1 * (2 * A + x1)) ]; | |
} | |
vanDerGrinten2.invert = function(x, y) { | |
if (!x) return [ 0, halfπ * Math.sin(2 * Math.atan(y / π)) ]; | |
var x1 = Math.abs(x / π), A = (1 - x1 * x1 - (y /= π) * y) / (2 * x1), A2 = A * A, B = Math.sqrt(A2 + 1); | |
return [ sgn(x) * π * (B - A), sgn(y) * halfπ * Math.sin(2 * Math.atan2(Math.sqrt((1 - 2 * A * x1) * (A + B) - x1), Math.sqrt(B + A + x1))) ]; | |
}; | |
(d3.geo.vanDerGrinten2 = function() { | |
return projection(vanDerGrinten2); | |
}).raw = vanDerGrinten2; | |
function vanDerGrinten3(λ, φ) { | |
if (Math.abs(φ) < ε) return [ λ, 0 ]; | |
var sinθ = φ / halfπ, θ = asin(sinθ); | |
if (Math.abs(λ) < ε || Math.abs(Math.abs(φ) - halfπ) < ε) return [ 0, π * Math.tan(θ / 2) ]; | |
var A = (π / λ - λ / π) / 2, y1 = sinθ / (1 + Math.cos(θ)); | |
return [ π * (sgn(λ) * asqrt(A * A + 1 - y1 * y1) - A), π * y1 ]; | |
} | |
vanDerGrinten3.invert = function(x, y) { | |
if (!y) return [ x, 0 ]; | |
var y1 = y / π, A = (π * π * (1 - y1 * y1) - x * x) / (2 * π * x); | |
return [ x ? π * (sgn(x) * Math.sqrt(A * A + 1) - A) : 0, halfπ * Math.sin(2 * Math.atan(y1)) ]; | |
}; | |
(d3.geo.vanDerGrinten3 = function() { | |
return projection(vanDerGrinten3); | |
}).raw = vanDerGrinten3; | |
function vanDerGrinten4(λ, φ) { | |
if (!φ) return [ λ, 0 ]; | |
var φ0 = Math.abs(φ); | |
if (!λ || φ0 === halfπ) return [ 0, φ ]; | |
var B = φ0 / halfπ, 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, λ0 = λ / halfπ, λ1 = λ0 + 1 / λ0, D = sgn(Math.abs(λ) - halfπ) * Math.sqrt(λ1 * λ1 - 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 * asqrt(F)) / (4 * B_C2 + D2); | |
return [ sgn(λ) * halfπ * x1, sgn(φ) * halfπ * asqrt(1 + D * Math.abs(x1) - x1 * x1) ]; | |
} | |
vanDerGrinten4.invert = function(x, y) { | |
if (!x || !y) return [ x, y ]; | |
y /= π; | |
var x1 = sgn(x) * x / halfπ, D = (x1 * x1 - 1 + 4 * y * y) / Math.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 = Math.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 -= δ = f / f_; | |
} while (δ > ε && --i > 0); | |
return [ sgn(x) * (Math.sqrt(D * D + 4) + D) * π / 4, halfπ * B ]; | |
}; | |
(d3.geo.vanDerGrinten4 = function() { | |
return projection(vanDerGrinten4); | |
}).raw = vanDerGrinten4; | |
var wagner4 = function() { | |
var A = 4 * π + 3 * Math.sqrt(3), B = 2 * Math.sqrt(2 * π * Math.sqrt(3) / A); | |
return mollweideBromley(B * Math.sqrt(3) / π, B, A / 6); | |
}(); | |
(d3.geo.wagner4 = function() { | |
return projection(wagner4); | |
}).raw = wagner4; | |
function wagner6(λ, φ) { | |
return [ λ * Math.sqrt(1 - 3 * φ * φ / (π * π)), φ ]; | |
} | |
wagner6.invert = function(x, y) { | |
return [ x / Math.sqrt(1 - 3 * y * y / (π * π)), y ]; | |
}; | |
(d3.geo.wagner6 = function() { | |
return projection(wagner6); | |
}).raw = wagner6; | |
function wagner7(λ, φ) { | |
var s = .90631 * Math.sin(φ), c0 = Math.sqrt(1 - s * s), c1 = Math.sqrt(2 / (1 + c0 * Math.cos(λ /= 3))); | |
return [ 2.66723 * c0 * c1 * Math.sin(λ), 1.24104 * s * c1 ]; | |
} | |
wagner7.invert = function(x, y) { | |
var t1 = x / 2.66723, t2 = y / 1.24104, p = Math.sqrt(t1 * t1 + t2 * t2), c = 2 * asin(p / 2); | |
return [ 3 * Math.atan2(x * Math.tan(c), 2.66723 * p), p && asin(y * Math.sin(c) / (1.24104 * .90631 * p)) ]; | |
}; | |
(d3.geo.wagner7 = function() { | |
return projection(wagner7); | |
}).raw = wagner7; | |
function wiechel(λ, φ) { | |
var cosφ = Math.cos(φ), sinφ = Math.cos(λ) * cosφ, sin1_φ = 1 - sinφ, cosλ = Math.cos(λ = Math.atan2(Math.sin(λ) * cosφ, -Math.sin(φ))), sinλ = Math.sin(λ); | |
cosφ = asqrt(1 - sinφ * sinφ); | |
return [ sinλ * cosφ - cosλ * sin1_φ, -cosλ * cosφ - sinλ * sin1_φ ]; | |
} | |
wiechel.invert = function(x, y) { | |
var w = -.5 * (x * x + y * y), k = Math.sqrt(-w * (2 + w)), b = y * w + x * k, a = x * w - y * k, D = Math.sqrt(a * a + b * b); | |
return [ Math.atan2(k * b, D * (1 + w)), D ? -asin(k * a / D) : 0 ]; | |
}; | |
(d3.geo.wiechel = function() { | |
return projection(wiechel); | |
}).raw = wiechel; | |
function winkel3(λ, φ) { | |
var coordinates = aitoff(λ, φ); | |
return [ (coordinates[0] + λ / halfπ) / 2, (coordinates[1] + φ) / 2 ]; | |
} | |
winkel3.invert = function(x, y) { | |
var λ = x, φ = y, i = 25; | |
do { | |
var cosφ = Math.cos(φ), sinφ = Math.sin(φ), sin_2φ = Math.sin(2 * φ), sin2φ = sinφ * sinφ, cos2φ = cosφ * cosφ, sinλ = Math.sin(λ), cosλ_2 = Math.cos(λ / 2), sinλ_2 = Math.sin(λ / 2), sin2λ_2 = sinλ_2 * sinλ_2, C = 1 - cos2φ * cosλ_2 * cosλ_2, E = C ? acos(cosφ * cosλ_2) * Math.sqrt(F = 1 / C) : F = 0, F, fx = .5 * (2 * E * cosφ * sinλ_2 + λ / halfπ) - x, fy = .5 * (E * sinφ + φ) - y, δxδλ = .5 * F * (cos2φ * sin2λ_2 + E * cosφ * cosλ_2 * sin2φ) + .5 / halfπ, δxδφ = F * (sinλ * sin_2φ / 4 - E * sinφ * sinλ_2), δyδλ = .125 * F * (sin_2φ * sinλ_2 - E * sinφ * cos2φ * sinλ), δyδφ = .5 * F * (sin2φ * cosλ_2 + E * sin2λ_2 * cosφ) + .5, denominator = δxδφ * δyδλ - δyδφ * δxδλ, δλ = (fy * δxδφ - fx * δyδφ) / denominator, δφ = (fx * δyδλ - fy * δxδλ) / denominator; | |
λ -= δλ, φ -= δφ; | |
} while ((Math.abs(δλ) > ε || Math.abs(δφ) > ε) && --i > 0); | |
return [ λ, φ ]; | |
}; | |
(d3.geo.winkel3 = function() { | |
return projection(winkel3); | |
}).raw = winkel3; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>D3 Geo Projection Plugin</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<script src="d3.geo.projection.js"></script> | |
</head> | |
<body> | |
<style> | |
body { | |
margin: 0; | |
} | |
svg { | |
background-color: #eee; | |
} | |
.sphere { | |
fill: #A7DBD8; | |
stroke: #79A09E; | |
stroke-width: 2; | |
} | |
.land { | |
fill: #E0E4CC; | |
stroke: #ACAF9F; | |
stroke-width: 1; | |
} | |
.graticule { | |
fill: none; | |
stroke: #79A09E; | |
stroke-width: 1; | |
} | |
</style> | |
<div id="map-container"></div> | |
<script> | |
// Set the dimensions of the map | |
var width = 960, | |
height = 480; | |
// Create a selection for the container div and append the svg element | |
var div = d3.select('#map-container'), | |
svg = div.append('svg'); | |
// Set the size of the SVG element | |
svg.attr('width', width).attr('height', height); | |
// Create and configure a geographic projection | |
var projection = d3.geo.hammer() | |
.translate([width / 2, height / 2]); | |
// Create and configure a path generator | |
var pathGenerator = d3.geo.path() | |
.projection(projection); | |
// Create and configure the graticule generator (one line every 20 degrees) | |
var graticule = d3.geo.graticule() | |
.step([20, 20]); | |
// Retrieve the geographic data asynchronously | |
d3.json('land.geojson', function(err, data) { | |
// Throw errors on getting or parsing the file | |
if (err) { throw err; } | |
// Shpere | |
var sphere = svg.selectAll('path.sphere').data([{type: 'Sphere'}]); | |
sphere.enter().append('path').classed('sphere', true); | |
sphere.attr('d', pathGenerator); | |
sphere.exit().remove(); | |
// Graticule lines (behind the land) | |
var lines = svg.selectAll('path.graticule').data([graticule()]); | |
lines.enter().append('path').classed('graticule', true); | |
lines.attr('d', pathGenerator); | |
lines.exit().remove(); | |
// Land | |
var land = svg.selectAll('path.land').data([data]); | |
land.enter().append('path').classed('land', true); | |
land.attr('d', pathGenerator); | |
land.exit().remove(); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Download and Transform the 1:50m Country Shapefiles from Natural Earth | |
# http://www.naturalearthdata.com/downloads/110m-physical-vectors/ | |
URL = http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/physical/ne_110m_land.zip | |
# Download the zip file from the Natural Earth server | |
ne_110m_land.zip: | |
curl -LO $(URL) | |
# Unzip the shapefiles | |
ne_110m_land.shp: ne_110m_land.zip | |
unzip ne_110m_land.zip | |
touch ne_110m_land.shp | |
# Convert the shapefiles to GeoJSON | |
land.geojson: ne_110m_land.shp | |
ogr2ogr -f GeoJSON land.geojson ne_110m_land.shp | |
# Convert the GeoJSON file to TopoJSON | |
land.topojson: countries.json | |
topojson -p -o land.topojson land.geojson | |
# Remove source and temporary files | |
clean: | |
rm ne_110m_land* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment