Chapter Five

The Station Node

From plain circles to pills that span tracks, with colored transfer dots inside.

Look at our four-line map from the last chapter. The lines are beautiful — parallel, glowing, smoothly splitting at junctions. But the stations are still plain white circles sitting on the centerline, ignoring the colored tracks flowing around them. The stations haven't caught up with the lines.

Real transit maps use different station markers depending on context. A minor stop on a single line gets a small dot. A major interchange where four lines cross gets a large elongated shape — a pill — that physically spans all the tracks, with colored indicators showing which lines stop there.

In this chapter we build three types of station markers, choosing the right one automatically based on how many lines serve the station.

Type 1: The Simple Dot

For stations served by a single line, a small circle is enough. We've been drawing these since Chapter 1:

Live Editor — Three station types

The top row shows the three station types side by side. The bottom row shows how the pill is constructed in four steps: tracks → pill rectangle on top → colored dots inside → label below.

The pill is just an SVG <rect> with rx/ry set to half its width, giving it fully rounded ends. Its height is computed from the number of lines × spacing, plus padding. The colored dots are tiny circles positioned at each line's offset within the pill.

Orienting the Pill

On a horizontal track, the pill is vertical — perpendicular to the direction of travel. On a diagonal track, the pill rotates to stay perpendicular. This is the same "perpendicular to the track" principle from Chapters 2 and 4.

Live Editor — Pills orient perpendicular to any track angle

The drawStation() function takes a position, a track angle, and the number of lines. It creates the pill using SVG's transform="rotate(...)" and places colored dots along the perpendicular axis. The same function handles all three angles without any special cases.

The Automatic Station Renderer

Now let's put it together — a function that automatically chooses the right station type based on the data:

Live Editor — Automatic: dot, pill, or transfer based on data

Each station now renders differently based on its data. Westfield and Park (1 line each) get simple dots. Riverside (2 lines) gets a plain pill. Old Town (3 lines) and Central (4 lines) get pills with colored transfer dots inside. The decision is automatic — the renderer just checks station.lines.length.

The Rule

1 line → small circle.
2 lines → pill (no dots — the two line colors are visible on either side).
3+ lines → pill with colored transfer dots inside.

This mirrors the convention used by most real-world transit maps. The threshold between "dot" and "pill" is a design choice — some systems use pills for all multi-line stations, others only for major interchanges. Adjust to taste.

The drawStation() Function

Let's extract the automatic station renderer into a clean, reusable function:

Live Editor — Reusable drawStation() function

The drawStation() function encapsulates all the logic: position, angle, line colors, spacing, and optional terminus flag. It returns the SVG string for the appropriate marker type. Combined with smoothPath() from Chapter 3 and the offset logic from Chapter 4, we now have the three core rendering functions:

smoothPath(points) — draws a smooth curve through stations
perpXY(ax, ay, bx, by, offset) — computes parallel line displacement
drawStation(x, y, angle, colors, spacing) — renders the right station marker

These three functions, plus the getSegmentLines() lookup from Chapter 4, are the entire rendering engine. Everything from here on is applying them to increasingly realistic data.

What We've Learned

Stations aren't just dots. They're data-driven markers that communicate how many lines serve them and which lines those are. The visual language is simple — dots for minor stops, pills for multi-line stations, colored dots inside pills for major interchanges — and the rendering function adapts automatically based on the station's line count.

The perpendicular-to-track principle appears for the third time: label placement (Ch.2), parallel line offset (Ch.4), and now pill orientation and dot placement (Ch.5). It's the one geometric idea you need for transit maps.

Next: what happens at junctions where lines meet, split, and merge in more complex ways.

Drawing Transit — An open guide to programmatic transit maps