Public
Edited
Apr 9
36 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
projection = d3
.geoIdentity(true)
.reflectY(true)
.fitExtent([[5, 5], [width - 5, height - 5]], geoMapPrepared)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
featuresGroupedByLineNumber = {
const segmentsPerLineNumber = new Map()
for (const f of tramMetroGeoData.features) {
String(f.properties.lijn)
.split(/ \| /g)
.forEach((number) => {
if (segmentsPerLineNumber.has(number)) {
segmentsPerLineNumber.get(number).push(f)
} else {
segmentsPerLineNumber.set(number, [f])
}
})
}
// These are double in roundabouts
segmentsPerLineNumber.delete("7E");
segmentsPerLineNumber.delete("13E");
return segmentsPerLineNumber
}
Insert cell
Insert cell
Insert cell
geoMap = {
const emptyFeatureCollection = {
type: "FeatureCollection",
features: []
}
for (const [lineNr, features] of featuresGroupedByLineNumber) {
console.group(lineNr)
const segments = features.map(f => f.geometry.coordinates)
const joinedOrderedSegments = new SegmentJoiner(segments).joinAllSegments()

// manually determined which geo lines need to be reversed to match their transit map path
if (reverseLines.includes(lineNr)) joinedOrderedSegments.reverse()
// save all lijnen on this track here for offset?
const geometry = {
"type": "Feature",
"geometry": { "type": "LineString", "coordinates": joinedOrderedSegments},
"properties": { ...features[0].properties, lijn: lineNr }
};
emptyFeatureCollection.features.push(geometry);
console.groupEnd()
}
return emptyFeatureCollection
}
Insert cell
Insert cell
Insert cell
Insert cell
// because making geoMap takes a while, just load a premade one
geoMapPrepared = FileAttachment("geoMap.json").json()
Insert cell
Insert cell
drawLegend = g => {
g.append("text")
.attr("y", -20)
.attr("font-size", "1.5em")
.attr("text-decoration", "underline")
.attr("font-weight", "bold")
.text("Tram");

g.append("text")
.attr("x", 80)
.attr("y", 200)
.attr("font-size", "1.5em")
.attr("text-decoration", "underline")
.attr("font-weight", "bold")
.text("Metro");

const legendEntries = g
.selectAll("line")
.data(colors)
.join("g")
.attr("transform", (d, i) =>
Number(d[0]) < 50
? `translate(0, ${i * 22})`
: `translate(80, ${(i - 5) * 22})`
);

legendEntries
.append("line")
.attr("stroke-width", 3)
.attr("x1", 0)
.attr("x2", 30)
.attr("stroke", d => d[1]);

legendEntries
.append("text")
.text(d => d[0])
.attr("x", 35)
.attr("fill", d => d[1])
.attr("stroke", "gainsboro")
.attr("alignment-baseline", "middle")
.attr("stroke-width", "1px");
}
Insert cell
Insert cell
Insert cell
d3 = require("d3@v6", "d3-interpolate-path@v2")
Insert cell
Insert cell
import { freelanceBanner } from "@julesblm/freelance-banner"
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