Public
Edited
Mar 11, 2023
Fork of D3 world map
1 star
Insert cell
Insert cell
Insert cell
map = {
const context = DOM.context2d(width, height);
const path = d3.geoPath(projection, context);
context.save();

// Draw Background, Graticule and Land
context.beginPath(), path(outline), context.clip(), context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
context.beginPath(), path(graticule), context.strokeStyle = "#ccc", context.stroke();
context.beginPath(), path(land), context.fillStyle = "#000", context.globalAlpha = 0.4, context.fill();
// Higlight each face with a random color.
const randomColor = d3.scaleOrdinal(d3.schemeTableau10)
basefaces.forEach((f, i) => {
// Fill Face
const poly = {type:"Polygon", coordinates: [[...f, f[0]]]};
const centroid = {type: "Point", coordinates: d3.geoCentroid(poly)};
context.beginPath(), path(poly), context.fillStyle = randomColor(i), context.lineWidth = 2,context.globalAlpha = 0.4, context.fill();
// Label Face
context.beginPath(), path.pointRadius(10)(centroid), context.globalAlpha = 1,
context.lineWidth = 1, context.fillStyle='white', context.strokeStyle='black', context.fill(), context.stroke();
context.fillStyle="black", context.strokeStyle="none", context.textBaseline="middle", context.textAlign="center",
context.fillText(i, projection(centroid.coordinates)[0], projection(centroid.coordinates)[1]);
});
context.restore();
// Stroke Outline
context.beginPath(), path(outline), context.strokeStyle = "#888", context.stroke();
return context.canvas;
}
Insert cell
projection = cuboctahedron()
Insert cell
function cuboctahedron(faceProjection) {

faceProjection = faceProjection || function(face) {
var c = d3.geoCentroid({type: "MultiPoint", coordinates: face});
return extraGeo.geoGnomonic()
.rotate([-c[0], -c[1]])
.scale(1)
.translate([0, 0])
};

var faces = basefaces.map(function(face, i) {
return {face: face, project: faceProjection(face), children: [], shared: []};
});
// Create spanning tree linking each face to its children.
faces[8].children = [12,4].map(d => faces[d]);
faces[4].children = [7].map(d => faces[d]);
faces[11].children = [13].map(d => faces[d]);
faces[6].children = [11,10].map(d => faces[d]);
faces[2].children = [6].map(d => faces[d]);
faces[5].children = [9,2].map(d => faces[d]);
faces[1].children = [8,5].map(d => faces[d]);
faces[0].children = [1,3].map(d => faces[d]);

function getFace(lambda, phi) {
// Find which face a given point on the sphere belongs to.
// This method is not super efficient but gets the job done.
const point = [lambda / Math.PI * 180, phi / Math.PI * 180];
for (let i = 0; i < faces.length; ++i) {
const poly = {type: "Polygon", coordinates: [[...faces[i].face, faces[i].face[0]]]};
if (d3.geoContains(poly, point)) return faces[i];
}
}

return extraGeo.geoPolyhedral(faces[0], getFace)
.angle(0)
.scale(100)
.center([0, 45]);
}
Insert cell
baseverts = [
[45, 45],[135, 45],[-135,45],[-45,45],
[0,0],[90,0],[-180,0],[-90,0],
[45, -45],[135, -45],[-135,-45],[-45,-45],
];
Insert cell
basefaces = [
[0,3,2,1],
[3,0,4],
[0,1,5],
[1,2,6],
[2,3,7],
[4,0,5,8],
[5,1,6,9],
[6,2,7,10],
[7,3,4,11],
[4,8,11],
[5,9,8],
[6,10,9],
[7,11,10],
[8,9,10,11]
].map(function(face) {
return face.map(function(i) {
return baseverts[i];
});
});
Insert cell
Insert cell
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, outline)).bounds(outline);
const dy = Math.ceil(y1 - y0);
const l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale(projection.scale() * (l - 1) / l).precision(0.2);
return dy;
}
Insert cell
path = d3.geoPath(projection)
Insert cell
outline = ({type: "Sphere"})
Insert cell
graticule = d3.geoGraticule10()
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
borders = topojson.mesh(world, world.objects.countries, (a, b) => a !== b)
Insert cell
world = FileAttachment("countries-50m.json").json()
Insert cell
extraGeo = require("d3-geo", "d3-geo-polygon")
Insert cell
testnet = {
// Just testing out that I've connected the right vertices together.
const line = d3.line();
return htl.svg`<svg viewBox="${-200} ${-100} 400 200" style="display: block;">
${basefaces.map((face, i) => {
return htl.svg.fragment`
<path stroke="#aaa" fill="none" d="${line(face.map(([x,y]) => [x, -y]))}" />
<text fill="red" text-anchor="middle" alignment-baseline="middle" font-size="8" x=${d3.mean(face, d=>d[0])} y=${-d3.mean(face, d=>d[1])}>${i}</text>`
})}
${baseverts.map(([x,y]) => htl.svg.fragment`<circle cx="${x}" cy="${-y}" r="2" fill="black"/>`)}
${baseverts.map(([x,y], i) => htl.svg.fragment`<text fill="blue"text-anchor="middle" x="${x}" y="${-y - 5}" r="2" font-size="6" fill="black">${i} (${x},${y})</text>`)}
</svg>`
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more