Public
Edited
May 8, 2023
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
morphing = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

const names = provincies.map((d) => d.properties.statnaam);

const text = svg
.append("text")
.attr("x", width / 3)
.attr("y", height - 30)
.attr("fill", "#c4528f")
.attr("font-size", "70px")
.attr("dominant-baseline", "baseline")
.attr("text-anchor", "middle");

svg
.append("path")
.datum(nlGeo)
.attr("d", geoPath)
.attr("stroke-width", 1)
.attr("stroke", "#ccc")
.attr("fill", "transparent");

const path = svg.append("path").attr("fill", "#c4528f");

yield svg.node();

for (let i = 0, n = pathStrings.length; true; i = (i + 1) % n) {
yield svg.node();
await path
.transition()
.ease(d3.easeLinear)
.duration(duration)
.attrTween("d", (d) =>
interpolatePaths(pathStrings[i], pathStrings[(i + 1) % n])
)
.delay(delay)
.end();

text.text(names[i + 1]);
}
}
Insert cell
nlGeo = topojson.feature(nl, nl.objects.provincie_2022)
Insert cell
provincies = nlGeo.features.reverse()
Insert cell
pathStrings = provincies.map(geoPath)
Insert cell
// https://observablehq.com/@neocartocnrs/shape-interpolations-with-flubber
interpolatePaths = (path1, path2) => {
const patharray1 = flubber.splitPathString(path1);
const patharray2 = flubber.splitPathString(path2);

// 1 to 1
if (patharray1.length == 1 && patharray2.length == 1) {
return flubber.interpolate(path1, path2);
}

// n to n
if (patharray1.length == patharray2.length) {
return flubber.interpolateAll(patharray1, patharray2);
}

// 1 to n
if (patharray1.length == 1 && patharray2.length > 1) {
return flubber.separate(path1, patharray2, {
maxSegmentLength: 20,
single: true
});
}

// n to 1
if (patharray1.length > 1 && patharray2.length == 1) {
return flubber.combine(patharray1, path2, {
maxSegmentLength: 20,
single: true
});
}

// n to m (with n > m)
if (patharray1.length > patharray2.length) {
const dif = patharray1.length - patharray2.length;
for (let i = 0; i < dif; i++) {
patharray2.push(patharray2[0]);
}
return flubber.interpolateAll(patharray1, patharray2, {
maxSegmentLength: 20,
single: true
});
}

// n to m (with n < m)
if (patharray1.length < patharray2.length) {
const dif = patharray2.length - patharray1.length;
for (let i = 0; i < dif; i++) {
patharray1.push(patharray1[0]);
}
return flubber.interpolateAll(patharray1, patharray2, {
maxSegmentLength: 20,
single: true
});
}
}
Insert cell
nl = FileAttachment("provincie_2022 (1).topojson").json()
Insert cell
height = 750
Insert cell
projection = d3
.geoMercator()
.fitSize([width, height], topojson.feature(nl, nl.objects.provincie_2022))
Insert cell
geoPath = d3.geoPath(projection)
Insert cell
Insert cell
flubber = require("flubber")
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