The Tetrahedral Gnomonic projection, published by A. J. Potter in The Geographical Teacher, Vol. 13, No. 1 (SPRING, 1925), pp. 52-56.
Using d3.geoPolyhedral on the tetrahedron
Gnomonic projection on each face
This is the “continental Earth" aspect given by Furuti.
There's a bug in this aspect: land and ocean are inversed. This will be fixed with issue #46: see this block. The Antarctic aspect doesn’t have this problem.
Research by Philippe Rivière for d3-geo-projection
issue 107.
forked from Fil's block: Tetrahedral Gnomonic projection
xxxxxxxxxx
<canvas width="960" height="600"></canvas>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="versor.js"></script>
<style>
path {fill: none; stroke: #444; }
</style>
<script>
var canvas = d3.select("canvas"),
width = canvas.property("width"),
height = canvas.property("height"),
context = canvas.node().getContext("2d");
// retina display
var devicePixelRatio = window.devicePixelRatio || 1;
canvas.style('width', canvas.attr('width')+'px');
canvas.style('height', canvas.attr('height')+'px');
canvas.attr('width', canvas.attr('width') * devicePixelRatio);
canvas.attr('height', canvas.attr('height') * devicePixelRatio);
context.scale(devicePixelRatio,devicePixelRatio);
var pi = Math.PI, degrees = 180 / pi, asin1_3 = Math.asin(1 / 3);
var centers = [
[0, 90],
[-180, -asin1_3 * degrees],
[-60, -asin1_3 * degrees],
[60, -asin1_3 * degrees]
];
d3.geoTetrahedralGnomonic = function(faceProjection) {
var tetrahedron = [[1, 2, 3], [0, 2, 1], [0, 3, 2], [0, 1, 3]].map(function(
face
) {
return face.map(function(i) {
return centers[i];
});
});
faceProjection =
faceProjection ||
function(face) {
var c = d3.geoCentroid({ type: "MultiPoint", coordinates: face });
return d3
.geoGnomonic()
.scale(1)
.translate([0, 0])
.rotate([-c[0] * (Math.abs(c[1]) != 90), -c[1]]);
};
var faces = tetrahedron.map(function(face) {
return { face: face, project: faceProjection(face) };
});
[-1, 0, 0, 0].forEach(function(d, i) {
var node = faces[d];
node && (node.children || (node.children = [])).push(faces[i]);
});
return d3
.geoPolyhedral(
faces[0],
function(lambda, phi) {
lambda *= degrees;
phi *= degrees;
for (var i = 0; i < faces.length; i++) {
if (
d3.geoContains(
{
type: "Polygon",
coordinates: [[...tetrahedron[i], tetrahedron[i][0]]]
},
[lambda, phi]
)
) {
return faces[i];
}
}
},
pi / 3
)
.clipAngle(180)
.rotate([-90, 180])
.fitExtent([[0, 0], [width, height]], { type: "Sphere" });
};
projection = d3.geoTetrahedralGnomonic();
var init_scale = projection.scale(),
path = d3.geoPath().projection(projection).context(context);
d3.json("https://unpkg.com/world-atlas@1/world/110m.json", function(
error,
world
) {
if (error) throw error;
var land = topojson.feature(world, world.objects.countries);
render = function() {
context.fillStyle = "#fff";
context.fillRect(0, 0, width, height);
context.beginPath();
path({type:"Sphere"});
context.strokeStyle = "black";
context.stroke(), context.clip(), context.closePath();
context.beginPath();
path(land);
context.fillStyle = "#000";
context.fill(), context.closePath();
context.beginPath();
path(d3.geoGraticule()());
context.strokeStyle = "#777";
context.stroke(), context.closePath();
};
render();
});
canvas.call(d3.drag().on("start", dragstarted).on("drag", dragged));
var render = function() {},
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0; // Projection rotation as versor at start.
function dragstarted() {
v0 = versor.cartesian(projection.invert(d3.mouse(this)));
r0 = projection.rotate();
console.log("r0", r0);
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(projection.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
projection.rotate(r1);
render();
}
</script>
https://d3js.org/d3.v4.js
https://d3js.org/d3-geo-projection.v2.min.js
https://d3js.org/topojson.v2.min.js