var ε = 1e-6, π = Math.PI, radians = π / 180, degrees = 180 / π, θ = Math.atan(Math.SQRT1_2) * degrees; // Converts spherical coordinates (degrees) to 3D Cartesian. function cartesian(coordinates) { var λ = coordinates[0] * radians, φ = coordinates[1] * radians, cosφ = Math.cos(φ); return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; } // Converts 3D cartesian to spherical coordinates (degrees). function spherical(cartesian) { var m = Math.sqrt(cartesian[0] * cartesian[0] + cartesian[1] * cartesian[1] + cartesian[2] * cartesian[2]); return [ Math.atan2(cartesian[1], cartesian[0]) * degrees, Math.asin(cartesian[2] / m) * degrees ]; } // map in a cube. (function() { var vertices = [ [0, θ], [90, θ], [180, θ], [-90, θ], [0, -θ], [90, -θ], [180, -θ], [-90, -θ] ]; var polyhedron = [ [0, 3, 2, 1], // N [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7], [4, 5, 6, 7] // S ].map(function(face) { return face.map(function(i) { return vertices[i]; }); }); var faces = polyhedron.map(function(face, i) { var centroid = d3.geo.centroid({type: "MultiPoint", coordinates: face}); if (Math.abs(Math.abs(centroid[1]) - 90) < ε) centroid[0] = 0; var ring = face.map(function(d) { return [((d[0] + 180) % 360 - 180) * radians, d[1] * radians]; }); face.centroid = centroid; return { face: face, polygon: [ring], project: d3.geo.gnomonic().scale(1).translate([0, 0]).rotate([-centroid[0], -centroid[1]]) }; }); // Connect each face to a parent face. [-1, 4, 5, 2, 0, 1].forEach(function (d, i) { var node = faces[d]; node && (node.children || (node.children = [])).push(faces[i]); }); var width = 900, height = 400, rotate = [30, 0, 0], rotation = d3.geo.rotation(rotate); var projection = d3.geo.polyhedron(faces[0], face, 1 * π / 2) .translate([ Math.atan(Math.SQRT1_2) * width / 2 + 45, height / 2]) // .translate([ width / 2, height / 2]) .rotate(rotate) // .center([10, 50]) .scale(90); var path = d3.geo.path().projection(projection); var svg = d3.select("#map").append("svg") .attr("width", width) .attr("height", height); svg.append("path") .datum({type: "Sphere"}) .attr("class", "background") .attr("d", path); svg.append("path") .datum(d3.geo.graticule()) .attr("class", "graticule") .attr("d", path); svg.append("path") .datum({type: "MultiLineString", coordinates: projection.mesh.map(function(segment) { return segment.map(rotation.invert); })}) .attr("class", "face") .attr("d", path); svg.append("path") .datum({type: "Sphere"}) .attr("class", "outline") .attr("d", path); d3.json("world-110m.json", function(error, world) { var land = topojson.feature(world, world.objects.land); svg.insert("path", ".graticule") .datum(land) .attr("class", "land") .attr("d", path); }); function face(λ, φ) { var point = [λ, φ], face = null, inside = 0; for (var i = 0, n = faces.length; i < n; ++i) { face = faces[i]; if (d3.geo.pointInPolygon(point, face.polygon)) return face; } } d3.select(self.frameElement).style("height", height + "px"); })();