// imports var atan = Math.atan, abs = Math.abs, ε = 1e-6, π = Math.PI, degrees = 180 / π, θ = atan(0.5) * degrees; // manually rotate/guessed from JvW article var r = [-85.76422660821149, -47.20427493656335, -83.97741129030045]; var d3Geo = d3, polyhedral = d3.geoPolyhedral; var width = 900, height = 400, margin = 5, rotate = [0, 0, 0]; // construction inspired by // https://en.wikipedia.org/wiki/Regular_icosahedron#Spherical_coordinates var vertices = [ [0, 90], [0, -90] ].concat(d3.range(10).map(function(i) { // var φ = i * 36; var φ = ((i * 36) + 180) % 360 - 180; return [φ, i & 1 ? θ : -θ]; })); var polyhedron = [ [ 0, 3, 11], [ 0, 5, 3], [ 0, 7, 5], [ 0, 9, 7], [ 0, 11, 9], // North [ 2, 11, 3], [ 3, 4, 2], [ 4, 3, 5], [ 5, 6, 4], [ 6, 5, 7], [ 7, 8, 6], [ 8, 7, 9], [ 9, 10, 8], [10, 9, 11], [11, 2, 10], // Equator [ 1, 2, 4], [ 1, 4, 6], [ 1, 6, 8], [ 1, 8, 10], [ 1, 10, 2] // South ].map(function(face) { return face.map(function(i) { return vertices[i]; }); }); // add centroid polyhedron.forEach(function(face) { face.centroid = d3Geo.geoCentroid({type: "MultiPoint", coordinates: face}); }); var jvw_icosahedron = function(faceProjection) { faceProjection = faceProjection || function(face) { var c = face.centroid; // AT THIS POINT IT'S STILL WORSE THAN GNOMONIC :) //return d3.geoGnomonic().rotate([-c[0], -c[1]]).scale(1).translate([0, 0]) return d3.geoThreePointRaw( d3Geo.geoAzimuthalEqualArea().rotate([-c[0], -c[1]]), ...face, 0.5) .scale(1).translate([0, 0]) //; }; var faces = polyhedron.map(function(face) { var polygon = face.slice(); polygon.push(polygon[0]); return { face: face, contains: function(lambda, phi) { return d3Geo.geoContains({ type: "Polygon", coordinates: [ polygon ] }, [lambda * degrees, phi * degrees]); }, project: faceProjection(face) }; }); // Connect each face to a parent face. [ // N -1, // 0 0, // 1 9, // 2 2, // 3 0, // 4 // Eq 0, // 5 5, // 6 6, // 7 7, // 8 8, // 9 9, // 10 10, // 11 11, // 12 4, // 13 5, // 14 // S 6, // 15 8, // 16 18, // 17 19, // 18 14, // 19 ].forEach(function (d, i) { var node = faces[d]; node && (node.children || (node.children = [])).push(faces[i]); }); function face(λ, φ) { for (var i = 0; i < faces.length; i++) { if (faces[i].contains(λ, φ)) return faces[i]; } } // Polyhedral projection var proj = polyhedral( faces[0], // the root face face, // a function that return a face give coords 2 // rotation of the root face in the projected (pixel) space ) .rotate(rotate) .clipAngle(-1) // call clipNone, see https://github.com/d3/d3-geo/pull/108#issuecomment-323798937 .fitExtent( [ [margin, margin], [width - margin, height - margin] ], {type:"Sphere"} ); proj.faces = faces; return proj; } d3.geoPolyhedralJakvanWijIcosahedron = jvw_icosahedron; // map in an icosahedron. var projection = d3.geoPolyhedralJakvanWijIcosahedron(); //var projection = d3.geoPolyhedralWaterman(), r = [1e-13,0.01]; var path = d3.geoPath().projection(projection); var graticule = d3.geoGraticule(); 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(graticule) .attr("class", "graticule") .attr("d", path); svg.append("path") .datum({type: "Sphere"}) .attr("class", "outline") .attr("d", path); // src1 does not work with rotation [0,0], but is ok with [1e-13,0] // src2 does not work with rotation [0,0], but is ok with [1e-13,0.01] // src3 is happy with rotation [0,0] var src1 = "https://unpkg.com/world-atlas/world/110m.json", src2 = "https://unpkg.com/world-atlas/world/50m.json", src3 = "world-110m.json", src = src3; // r = [0,0]; // r = [1e-13,0]; // r = [1e-13,0.01]; d3.json(src, function(error, world) { var land = topojson.feature(world, world.objects.land); svg .insert("path", ".graticule") .datum(land) .attr("class", "land") .attr("d", path); }); var render = function() { svg.selectAll('path').attr('d', path); }; console.log(projection.clipAngle(), projection.clipPolygon()) projection.rotate(r) && render() d3.select(self.frameElement).style("height", height + "px");