**Orthographic represntation of the World and gnomonic projection of the World on the five platonic solids foldout.
Inspiration from:
xxxxxxxxxx
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="platonic.css">
<style>
</style>
<title>Earth on platonic solids</title>
</head>
<body>
<div id="polyhedron"><select id="polyhedron-menu"></select></div>
<div id="comment"></div>
<div class="flex-container">
<div id="rotating_globe"></div>
<div id="rotating_polyhedron"></div>
<div id="flatten_polyhedron"></div>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="d3-geo-projection.js"></script>
<script src="d3-geo.js"></script>
<script src="platonic_solids.js"></script>
<script>
var velocity = [.025, .025],
t0 = Date.now();
var width =400, height = 400, margin = 1;
var polyhedron = [{name: "icosahedron"}, {name: "dodecahedron"}, {name: "octahedron"}, {name: "hexahedron"}, {name: "tetrahedron"}];
var menu = d3.select("#polyhedron-menu")
.on("change", change);
menu.selectAll("option")
.data(polyhedron)
.enter().append("option")
.text(function(d) { return d.name; });
var canvas = d3.select("#rotating_globe").append("canvas")
.attr("width", width - 100)
.attr("height", height);
var context = canvas.node().getContext("2d");
var svg_polyhedron = d3.select("#rotating_polyhedron").append("svg")
.attr("width", width - 100)
.attr("height", height);
var svg_flatten = d3.select("#flatten_polyhedron").append("svg")
.attr("width", width)
.attr("height", height);
var comment = d3.select("#comment");
// Define here how to project the globe on each face of an icosahedron
// We choose a gnomonic projection centered on the face's centroid
var faceProjection = function(face) {
var c = d3.geoCentroid({type: "MultiPoint", coordinates: face});
if (Math.abs(Math.abs(c[1]) - 90) < 1e-6) c[0] = 0;
return d3.geoGnomonic().scale(1).translate([0, 0]).rotate([-c[0], -c[1]]);
};
var projection = d3.geoOrthographic()
// translate the origin of the map to [0,0] as a start, not to the now meaningless default of [480,250]
.translate([(width-100) / 2, height / 2])
.clipAngle(90)
.precision(.1)
.scale(140);
var path = d3.geoPath()
.projection(projection)
.context(context);
var graticule = d3.geoGraticule();
var grid = graticule();
update(0);
function update(index) {
var p = polyhedron[index].name;
d3.json("world-110m.json", function(error, world){
var land = topojson.feature(world, world.objects.land);
d3.timer(function(elapsed) {
var time = Date.now() - t0;
projection.rotate([velocity[0] * elapsed, velocity[1] * elapsed]);
//projection.rotate([time * velocity[0], time * velocity[1]]);
face
.each(function(d) { d.polygon = d.map(projection); })
.attr("d", function(d) { return 'M' + d.polygon.join('L') + 'Z'; });
svg_polyhedron.append("path")
.datum({type: "Sphere"})
.attr("class", "outline")
.attr("d", path);
context.clearRect(0, 0, width, height);
// dessine le cercle périphérique simulant le contour du globe
context.beginPath();
path({type: "Sphere"});
context.lineWidth = 1;
context.setLineDash([1,0]);
context.strokeStyle = "#000";
context.stroke();
// dessine le disque equivalent au outline
context.beginPath();
path({type: "Sphere"});
context.fillStyle = "#ccf";
context.fill();
// dessine les continents
context.beginPath();
path(land);
context.lineWidth = 1;
context.strokeStyle = 'black';
context.stroke();
context.fillStyle = '#9f9';
context.fill();
// dessine le graticule
context.beginPath();
path(graticule());
context.lineWidth = .25;
context.setLineDash([1,0]);
context.strokeStyle = "rgba(0, 0, 0, 0.5)";
context.stroke();
context.beginPath();
path({type: "MultiPolygon", coordinates: d3[p].faces.map(function(face) {
face = face.map(d3.geoRotation([168, 0]));
face.push(face[0]);
return [face];
})
});
context.lineWidth = .5;
context.setLineDash([8, 12]);
context.strokeStyle ="#FF0000";
context.stroke();
});
});
comment.append("text")
.text('height = ' + height + ' width = ' + width);
var faceGroup = svg_polyhedron.append("g");
// Add the triangular face
face = faceGroup.selectAll(".face")
.data(d3[p].faces)
.enter().append("path")
.attr("class", "face");
var projection_flatten = d3[p].projection(faceProjection);
projection_flatten.fitExtent([ [margin, margin], [(height-margin)*d3.icosahedron.sizeratio, (height-margin)] ], {type:"Sphere"});
projection_flatten.rotate([0,0,0]);
svg_flatten
.attr("width", (height-margin)*d3.icosahedron.sizeratio);
var path_flatten = d3.geoPath().projection(projection_flatten);
var graticule_flatten = d3.geoGraticule();
svg_flatten.append("path")
.datum({type: "Sphere"})
.attr("class", "background")
.attr("d", path_flatten);
svg_flatten.append("path")
.datum(graticule_flatten)
.attr("class", "graticule")
.attr("d", path_flatten);
d3.json("world-110m.json", function(error, world) {
var land = topojson.feature(world, world.objects.land);
svg_flatten
.insert("path", ".graticule")
.datum(land)
.attr("class", "land")
.attr("d", path_flatten);
});
svg_flatten.append("path")
.datum({type: "MultiPolygon", coordinates: d3[p].faces.map(function(face) {
var rot = d3.geoRotation([0,0,0]);
var face_rot = [];
for (var i = 0; i < face.length; ++i) {
face_rot.push(rot(d3.geoInterpolate(face.centroid, face[i])(0.995)));
}
face_rot.push(rot(d3.geoInterpolate(face.centroid, face[0])(0.995)));
return [face_rot];
})})
.attr("class", "face")
.attr("d", path_flatten);
svg_flatten.append("path")
.datum({type: "Sphere"})
.attr("class", "outline")
.attr("d", path_flatten);
}
function change(d){
svg_flatten.selectAll("*").remove();
svg_polyhedron.selectAll("*").remove();
comment.selectAll("*").remove();
update(this.selectedIndex);
}
</script>
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v2.min.js