Public
Edited
Apr 26, 2019
3 stars
Insert cell
Insert cell
Insert cell
{
// Prepare SVG. width is auto defined. height is a variable.
const svg = d3.select(DOM.svg(width, height)).style("overflow", "visible");
// Set a background. Find bgColor to change.
const background = svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('fill', bgColor)
// fn createLine: creates and appends the metro line
// inputs: band(the yAxis label), length (of the line) and color
// output: appends the line directly
// extra params: metroLineWidth is set in the params section
const createLine = (band, length, color)=> {
return svg.append('line')
.attr('x1',xScale(0)+stationR/2)
.attr('y1',yScale(band))
.attr('x2',xScale(length)-stationR/2)
.attr('y2',yScale(band))
.attr('style',`stroke:${color};stroke-width:${metroLineWidth};`)
.attr('stroke-linecap','round')
}
// fn lineLen: calculates metro line length from array of stations
// input: array of stations
// output: line length in float
// extra params: staionLen is set in the params section
const lineLen = (lineStations) => {
// Formula: distance of the last station from the first + ( station length * nb of stations)
return lineStations[lineStations.length-1].distance_first + lineStations.length*stationLen
}
const guideLine = svg.append('line')
.attr('x1',xScale(0))
.attr('y1',0)
.attr('x2',xScale(0))
.attr('y2',height)
.attr('style','stroke:white;stroke-width:1;opacity:0.2;visibility:hidden')
const guideText = svg.append('text')


svg.on("mousemove", function () {
const mouse = d3.mouse(this)
if( mouse[0] >= margin.left && mouse[0] <= width-margin.right) {
guideLine.style('visibility','visible').attr('x1', mouse[0]).attr('x2', mouse[0])
guideText.style('visibility','visible')
guideText.text(`${(xScale.invert(mouse[0])/1000).toFixed(2)}km`)
.attr("x", mouse[0]+10)
.attr("y", mouse[1])
.style("font", "11px sans-serif")
.style("fill", 'white')
}else{
guideLine.style('visibility','hidden');
guideText.style('visibility','hidden')
}
})
.on("mouseout", ()=>{
guideLine.style('visibility','hidden')
guideText.style('visibility','hidden')
});

// Creates all lines. Maybe should do with data joining ? oops.
const lines = {};
for (let line of [1,2,4,5]) {
let lineStations = selectStationsByLine(d3.csvParse(stationsData, d3.autoType), line)
lines[line] = createLine(line, lineLen(lineStations), lineColors[line])
}
// Creates and appends placeholder for tooltip
const tooltip = svg.append("g");
// Creates and appends all stations based on data
// Design decision: since the distance data doesn't seem to account for station length, I've decided to add 150m * nb of stations for each line. 150m is a ballbark estimate of a station length. The azur trains are around 152m long. There is probably some variation in length but I did not find that data. The station circles in the diagram are placed at the center of that 150m zone representing station. I've experimenting with adding 150m lines instead of circles but when the diagram is too small the lines are longer than 150m due to the rounded linecaps.
// . 150m
// |------------------------|-O-|------------------------|-O-|------------------------|
// dist between stations |
// from data circle placement
const stations = svg.append('g')
.selectAll('line')
.data(d3.csvParse(stationsData, d3.autoType))
.enter()
.append('circle')
//Formula: distance from first + stationLen * nb of station before current + stationLen/2
.attr('cx', d => xScale(d.distance_first+(stationLen/2)+(stationLen*(d.order-1)) ))
.attr('cy', d => yScale(d.line) )
.attr('r', stationR )
.attr('fill',stationColor)
.attr('id',d => `station-${d.line}-${d.order}`)
.on("mouseover", d =>
tooltip
.attr("transform", `translate(${xScale(d.distance_first+(stationLen/2)+(stationLen*(d.order-1)) )},${yScale(d.line)-metroLineWidth/2})`)
.call(callout, d.station)
)
.on('mouseout', d => tooltip.call(callout, null))

return svg.node()
}
Insert cell
Insert cell
Insert cell
xScale = d3.scaleLinear()
.rangeRound([0+margin.left,width-margin.right])
.domain([0,30000])
Insert cell
Insert cell
Insert cell
height=300
Insert cell
margin = ({top: 50, right: 40, bottom: 30, left: 40})
Insert cell
Insert cell
metroLineWidth = 16
Insert cell
stationLen=150
Insert cell
Insert cell
stationColor = 'white'
Insert cell
stationR = metroLineWidth/3
Insert cell
Insert cell
Insert cell
Insert cell
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