Unit 2 - Branching Line

In this guide, we will cover branching lines utilizing Copenhagen's M1 and M2 as an example. We will showcase a method for doing this where the stations are rendered as if they were still one single line. In Unit 3, we will cover doing proper 'splits' like in the Helsinki example.

Please note that we are using text icons for this particular strip map in order to highlight their usage. It may be preferable to use the proper icons if available. In addition, connections to the Danish State Railways (Danske Statsbaner) are labelled with DSB for short.

Initial Setup

We will begin by placing two parallel lines - one for each of the two services (M1 and M2). We will place M2 on the base line and shift M1 over by 16 pixels, so that it is not necessary to move the stations or their names.

In regards to the stations, we will define three station types - one for M1 alone, one for M2 alone, and one for both of them combined.

For now, we will not have the actual shifting of the lines and will simply have the two lines as-is, with the station icons being used to denote which service stops where.

    "linename": "Copenhagen Metro",
    "iconID": ["ICON_M1", "ICON_M2"],
    "maincustomsvgbg": "<text x='16' y='460' fill='#AAAAAA' text-anchor='start' dominant-baseline='central'>01/2019</text>",
    "strokes": [
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "endpoint": 15
        }
    ],

Above we utilize the endpoint field to truncate M2 to its terminus. As an aside, note the usage of the "iconID" array for displaying both icons side by side.

For now we have relatively simple stations. Those for M1 have their circle component shifted by 16 pixels. The combined station shares this feature as it combines both nodes from the M1 and M2 stations. Since we've placed our M1 line 16 pixels below, we also move the icon offsets by 32 (default is 16) to prevent the icons from rendering on top of our M1 line.

    "stationtypes": [
        {
            "stypeID": "STATION_TYPEM1",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "white",
                    "scolor": "none",
                    "dy": 16
                }
            ],
            "stniconoffset": 32
        },
        {
            "stypeID": "STATION_TYPEM2",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "black",
                    "scolor": "none"
                }
            ],
            "stniconoffset": 32
        },
        {
            "stypeID": "STATION_TYPE_COMBINED",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "black",
                    "scolor": "none"
                },
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "white",
                    "scolor": "none",
                    "dy": 16
                }
            ],
            "stniconoffset": 32
        }
    ],

Now, while this current strip map isn't bad by any means, it can result in confusion for those who may assume that M1 runs along with M2 but simply does not stop past Lufthavnen/Airport Station. For this reason, we will split the lines physically on the map after the station.

Splitting the Lines

We will split in two parts. First, we will break the lines between Christianshavn and Amagerbro using startpoint and endpoint, and move the stations and their icons.

    "linename": "Copenhagen Metro",
    "iconID": ["ICON_M1", "ICON_M2"],
    "maincustomsvgbg": "<text x='16' y='460' fill='#AAAAAA' text-anchor='start' dominant-baseline='central'>01/2019</text>",
    "strokes": [
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "endpoint": 8.25
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "endpoint": 8.25
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": -16
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "startpoint": 8.75,
            "startlinkheight": 16,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "startpoint": 8.75,
            "endpoint": 15,
            "startlinkheight": -16,
            "endlinkheight": -16
        }
    ],

We took the original lines and cut them off at 8.25, then added connecting segments with an endlinkheight, and finally the 'lone' segments. These have a "startlinkheight" field set, as it would default to 0 otherwise.

As for the stations, we have applied "dy" shifts like so:

    {
            "name": ["Lufthavnen"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
                ["DSB"]
            ],
            "dy": -16
        },
        {
            "name": ["Islands Brygge"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        },

In total, we shifted M1 down by 16px and M2 up by 16px. Thus M2's stations have been shifted up accordingly, and M1s have been shifted down.

Aesthetic Improvements

Now, one thing to keep in mind is that for this map, we are using a "square" linecap on the paths that comprise the main lines. If you join two "butt" or "square" paths at an angle, you end up with an ugly transition, as can be seen above. We will address this using intermediate paths with a "round" linecap.

    "strokes": [
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "endpoint": 8
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "endpoint": 8
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8,
            "endpoint": 8.25
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8,
            "endpoint": 8.25
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": -16
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8.75,
            "endpoint": 9,
            "startlinkheight": 16,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8.75,
            "endpoint": 9,
            "startlinkheight": -16,
            "endlinkheight": -16
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "startpoint": 9,
            "startlinkheight": 16,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "startpoint": 9,
            "endpoint": 15,
            "startlinkheight": -16,
            "endlinkheight": -16
        }
    ],

While this contains many more paths (10 total - 5 per line), it makes the transition smoother.

The code used to generate this final map is provided below (with the same brevity notes as before for the "maincustomsvgbg" field).

var obj_line2 = {
    "linename": "Copenhagen Metro",
    "iconID": ["ICON_M1", "ICON_M2"],
    "maincustomsvgbg": "01/2019",
    "strokes": [
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "endpoint": 8
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "endpoint": 8
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8,
            "endpoint": 8.25
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8,
            "endpoint": 8.25
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8.25,
            "endpoint": 8.75,
            "endlinkheight": -16
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "round",
            "dy": 16,
            "startpoint": 8.75,
            "endpoint": 9,
            "startlinkheight": 16,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "round",
            "startpoint": 8.75,
            "endpoint": 9,
            "startlinkheight": -16,
            "endlinkheight": -16
        },
        {
            "color": "#397c5d",
            "strokewidth": "12px",
            "linecap": "square",
            "dy": 16,
            "startpoint": 9,
            "startlinkheight": 16,
            "endlinkheight": 16
        },
        {
            "color": "#ebd043",
            "strokewidth": "12px",
            "linecap": "square",
            "startpoint": 9,
            "endpoint": 15,
            "startlinkheight": -16,
            "endlinkheight": -16
        }
    ],
    "stationtypes": [
        {
            "stypeID": "STATION_TYPEM1",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "white",
                    "scolor": "none"
                }
            ]
        },
        {
            "stypeID": "STATION_TYPEM2",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "black",
                    "scolor": "none"
                }
            ]
        },
        {
            "stypeID": "STATION_TYPE_COMBINED",
            "stnnodes": [
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "black",
                    "scolor": "none"
                },
                {
                    "stationradius": 4,
                    "stationstrokewidth": 0,
                    "fcolor": "white",
                    "scolor": "none",
                    "dy": 16
                }
            ],
            "stniconoffset": 32
        }
    ],
    "stations": [
        {
            "name": ["Vanløse"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
                ["S-tog"]
            ]
        },
        {
            "name": ["Flintholm"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
                ["S-tog"]
            ]
        },
        {
            "name": ["Lindevang"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Fasanvej"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Frederiksberg"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Forum"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Nørreport"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
                ["S-tog"],
                ["DSB"]
            ]
        },
        {
            "name": ["Kongens Nytorv"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Christianshavn"],
            "stationtype": "STATION_TYPE_COMBINED",
            "icons": [
            ]
        },
        {
            "name": ["Amagerbro"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Lergravsparken"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Øresund"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Amager Strand"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Femøren"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Kastrup"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
            ],
            "dy": -16
        },
        {
            "name": ["Lufthavnen"],
            "stationtype": "STATION_TYPEM2",
            "icons": [
                ["DSB"]
            ],
            "dy": -16
        },
        {
            "name": ["Islands Brygge"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        },
        {
            "name": ["DR Byen"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        },
        {
            "name": ["Sundby"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        },
        {
            "name": ["Bella Center"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        },
        {
            "name": ["Ørestad"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
                ["DSB"]
            ],
            "dy": 32
        },
        {
            "name": ["Vestamager"],
            "stationtype": "STATION_TYPEM1",
            "icons": [
            ],
            "dy": 32
        }
    ]
};