function drawLabels(context, contour) {
const width = parseInt(context.canvas.style.width),
height = (context.canvas.height / context.canvas.width) * width,
scale = 1,
path = d3.geoPath().context(context);
const threshold = contour.value,
labels = [],
steps = 30;
contour.coordinates.forEach(polygon =>
polygon.forEach((ring, j) => {
const p = ring.slice(1, Infinity),
possibilities = d3.range(steps, steps * 1.4),
scores = possibilities.map(d => -((p.length - 1) % d)),
n = possibilities[d3.scan(scores)],
start = 1 + (d3.scan(p.map(xy => (j === 0 ? -1 : 1) * xy[1])) % n),
margin = 2;
if (p.length < 15) return;
p.forEach((xy, i) => {
if (
i % n === start &&
xy[0] > margin &&
xy[0] < width - margin &&
xy[1] > margin &&
xy[1] < height - margin
) {
const a = (i - 2 + p.length) % p.length,
b = (i + 2) % p.length,
dx = p[b][0] - p[a][0],
dy = p[b][1] - p[a][1];
if (dx === 0 && dy === 0) return;
labels.push({
threshold,
xy: xy.map(d => scale * d),
angle: Math.atan2(dy, dx),
text: `${threshold}`
});
}
});
})
);
context.save();
context.beginPath();
context.moveTo(0, 0),
context.lineTo(width, 0),
context.lineTo(width, height),
context.lineTo(0, height),
context.lineTo(0, 0);
const arc = d3.arc();
for (const label of labels) {
for (let i = 0; i < 2 * Math.PI; i += 0.2) {
const pos = [Math.cos(i) * 13, -Math.sin(i) * 10],
c = Math.cos(label.angle),
s = Math.sin(label.angle);
context[i === 0 ? "moveTo" : "lineTo"](
label.xy[0] + pos[0] * c - pos[1] * s,
label.xy[1] + pos[1] * c + pos[0] * s
);
}
}
context.clip();
context.beginPath();
path(contour);
context.stroke();
context.restore();
for (const label of labels) {
addlabel(context, label);
}
function addlabel(context, label) {
context.save();
context.translate(...label.xy);
context.rotate(label.angle + (Math.cos(label.angle) < 0 ? Math.PI : 0));
context.fillText(label.text, -1, 4);
context.restore();
}
}