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] }) }) polyhedron.map(function (face, i) { face.centroid = d3.geo.centroid({type: 'MultiPoint', coordinates: face}) }) // Split face 5 at centroid. var f = polyhedron[5] var corners = f.slice() var centroid = f.centroid f.pop() f.pop() f.push(centroid) polyhedron.push(f = [corners[1], corners[2], centroid]) f.centroid = centroid polyhedron.push(f = [corners[2], corners[3], centroid]) f.centroid = centroid polyhedron.push(f = [corners[3], corners[0], centroid]) f.centroid = centroid var faces = polyhedron.map(function (face, i) { var centroid = face.centroid if (Math.abs(Math.abs(centroid[1]) - 90) < ε) centroid[0] = 0 face.centroid = centroid var ring = face.map(function (d) { return [((d[0] + 180) % 360 - 180) * radians, d[1] * radians] }) 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, // North pole 0, // 1 0, // 2 0, // 3 0, // 4 1, // 5 first slice of South Pole 2, // 6 second slice of South Pole 3, // 7 third slice of South Pole 4 // 8 fourth slice of South Pole ].forEach(function (d, i) { var node = faces[d] node && (node.children || (node.children = [])).push(faces[i]) }) var width = 600, height = 600, rotate = [82.06, -22.868, 39.66], center = [width / 2, height / 2], rotation = d3.geo.rotation(rotate) var projection = d3.geo.polyhedron(faces[0], face, -1 * π / 2) .translate([width / 2, height / 2]) .rotate(rotate) .center(center) .scale(80) 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') })()