# trainmap

`trainmap` is a LaTeX + TikZ package for drawing **schematic railway and
transit maps** declaratively, in the style of the FGC / Rodalies line
diagrams. Instead of positioning every node by hand, you describe a line or a
whole network with a small, readable set of commands — `\addstation`,
`\addterminal`, `\forkup`, `\branch`, and so on — and the package takes care of
the geometry.

<p align="center">
  <img src="docs/preview_rodalies_r7.png" alt="Rodalies R7 line produced with trainmap" width="92%">
</p>

## Table of contents

- [Features](#features)
- [The two map styles (`mode`)](#the-two-map-styles-mode)
- [Documentation](#documentation)
- [Installation](#installation)
- [Using it in your editor (TeXstudio)](#using-it-in-your-editor-texstudio)
- [Quick start](#quick-start)
- [Long lines: the serpentine layout](#long-lines-the-serpentine-layout)
- [Command reference](#command-reference)
- [Configuration keys](#configuration-keys)
- [Predefined colours](#predefined-colours)
- [How the engine works](#how-the-engine-works)
- [Repository layout](#repository-layout)
- [License](#license)

## Features

- **Two map styles.** An **FGC** style that shows every line at once (with a
  line badge on each terminal), and a **Rodalies** style for a single line
  whose code is shown on a square plate at each end. Both still show
  interchanges. Selected with the `mode` key.
- **Key–value configuration** through `pgfkeys`, all under the `/trainmap/`
  namespace: line colours, stroke widths, label angles and station sizes. Any
  key can be set once for the whole map or overridden on an individual station.
- **Common trunk of parallel tracks.** Several lines can run together as a
  stacked bundle and then branch off or join at different heights.
- **Multi-line station names.** Break a long name across lines with `\\`.
- **Standard stations** drawn as a perpendicular tick crossing the whole
  bundle, with a rotated label (45° by default, configurable).
- **Terminal / head stations** drawn as a larger filled disc, a **bold** name
  and a coloured **badge** carrying the line code (for example `S1`, `L6`).
- **Interchange stations** drawn as a rounded capsule that groups all the
  parallel tracks, for stops shared by several services.
- **Branches and forks** with smoothly curved diagonal connectors:
  `\forkup`, `\forkdown`, `\joinline`, `\branch`, `\track`.
- **Automatic serpentine (snake) wrapping** for lines that are too long to fit
  on one row: set a maximum width and the line folds onto stacked rows joined
  by nested U-turns.
- **Two ways to load it:** as a LaTeX package (`\usepackage{trainmap}`) or as a
  TikZ library (`\usetikzlibrary{trainmap}`).

## The two map styles (`mode`)

The `mode` key selects how the diagram is presented.

### `mode=fgc` (default) — several lines at once

Every line of the trunk is drawn. Each `\addterminal` shows a filled disc, the
bold station name, and the line badge **below the disc**; interchanges are drawn
as capsules. Use this when several services share the diagram (the classic FGC
schematic look).

<p align="center">
  <img src="docs/preview_fgc_style_map.png" alt="FGC-style multi-line map" width="92%">
</p>

Full source: [`examples/fgc_style_map.tex`](examples/fgc_style_map.tex).

### `mode=rodalies` — a single line

Only one line is drawn. Its code is shown as a **square plate at each end** of
the whole route, with the line starting right next to the plate — so terminals
no longer carry an individual badge. Station names appear as usual and shared
stops are still marked as correspondences with `\addinterchange`. The code for
the end plates is taken from `line code`, or automatically from the first
`\addterminal` if you do not set it.

```latex
\begin{trainmap}[mode=rodalies, badge color=tmR4]
  \settrunk{0/tmR4}
  \addterminal{Sant Vicenç de Calders}{R4}  % opening [R4] plate
  \addstation{El Vendrell}
  % ...
  \addinterchange{Barcelona Sants}          % a correspondence
  % ...
  \addterminal{Manresa}{R4}                  % closing [R4] plate
\end{trainmap}
```

Because Rodalies lines tend to be long, this mode is normally combined with the
[serpentine layout](#long-lines-the-serpentine-layout). See
[`examples/rodalies_r4.tex`](examples/rodalies_r4.tex) for the full R4 line.

## Documentation

The full user manual is included as **[`trainmap.pdf`](trainmap.pdf)** (source:
[`trainmap-doc.tex`](trainmap-doc.tex)). It covers installation, the complete
command and key reference, and worked examples with rendered diagrams.

## Installation

The package is made of two files that must always travel together:

| File | Role |
|------|------|
| `trainmap.sty` | LaTeX wrapper — enables `\usepackage{trainmap}`. |
| `tikzlibrarytrainmap.code.tex` | The implementation — loaded by `\usetikzlibrary{trainmap}`. |

### Option A — from CTAN (once published)

`trainmap` is prepared for distribution on [CTAN](https://ctan.org). Once it is
available there, install it with your TeX distribution's package manager:

```bash
tlmgr install trainmap      # TeX Live
mpm --install=trainmap      # MiKTeX (or use the MiKTeX Console)
```

Most distributions also ship it in their package repositories, so it may already
be installed. After that, `\usepackage{trainmap}` just works.

### Option B — next to your document (quickest)

Copy **both** files into the same folder as your `.tex` file and compile with
`pdflatex`. LaTeX always searches the document's own folder first, so nothing
else is required.

### Option C — install into your personal `texmf` tree (available everywhere)

```bash
# 1. Find your personal texmf root
kpsewhich -var-value TEXMFHOME
#   Linux/macOS: usually ~/texmf
#   Windows/MiKTeX: usually C:\Users\<user>\AppData\Roaming\MiKTeX

# 2. Create the destination folder and copy both files
mkdir -p "$(kpsewhich -var-value TEXMFHOME)/tex/latex/trainmap"
cp trainmap.sty tikzlibrarytrainmap.code.tex \
   "$(kpsewhich -var-value TEXMFHOME)/tex/latex/trainmap/"

# 3. Refresh the filename database
mktexlsr                 # TeX Live
# MiKTeX: open the MiKTeX Console -> Tasks -> Refresh file name database
#         (or run:  initexmf --update-fndb)
```

On Windows you can copy the files manually instead of using the shell; the
destination folder is
`C:\Users\<user>\AppData\Roaming\MiKTeX\tex\latex\trainmap\`.
After a database refresh, `\usepackage{trainmap}` works from any document.

## Using it in your editor (TeXstudio)

**Compiling.** Build with **pdfLaTeX**. In TeXstudio press **F5** (build and
view) or **F6** (build only). If the default compiler is not pdfLaTeX, set it
under *Options → Configure TeXstudio → Build → Default Compiler → PdfLaTeX*.

**Removing the orange highlighting.** By default TeXstudio underlines any
command it does not know — so `\addstation`, `\addterminal`, etc. appear in a
warning colour even though the document compiles perfectly. This is only a
cosmetic check in the editor, not an error. To make TeXstudio recognise the
`trainmap` commands, install the bundled completion file `trainmap.cwl`:

1. In TeXstudio open *Help → Show Manual* is not needed — instead go to
   *Options → Configure TeXstudio → Completion* and note the user completion
   directory, or simply copy `trainmap.cwl` into TeXstudio's completion folder:
   - Windows: `C:\Users\<user>\AppData\Roaming\texstudio\completion\user\`
   - Linux: `~/.config/texstudio/completion/user/`
   - macOS: `~/Library/Application Support/texstudio/completion/user/`
2. Restart TeXstudio.

Once the file is in place and your document contains `\usepackage{trainmap}`,
the commands are treated as known and the orange highlighting disappears; you
also get autocompletion for the commands and their keys.

## Quick start

```latex
\documentclass[border=10pt]{standalone}
\usepackage{trainmap}

\begin{document}
% mode=rodalies -> a square [S1] plate at each end of the line
\begin{trainmap}[mode=rodalies, station step=2.0, badge color=tmS1]
  \settrunk{0/tmS1}                   % a single orange track (S1)
  \addterminal{Pl. Espanya}{S1}       % first end (sets the line code)
  \addstation{Magòria}
  \addstation{Ildefons Cerdà}
  \addinterchange{Sant Josep}         % a correspondence / transfer
  \addstation[label position=below]{Gornal}
  \addstation{L'Hospitalet\\de Llobregat}   % \\ = manual line break
  \addterminal{Cornellà}{S1}          % last end (closing plate)
\end{trainmap}
\end{document}
```

<p align="center">
  <img src="docs/preview_simple_line.png" alt="Simple S1 line, Rodalies style" width="86%">
</p>

Full source: [`examples/simple_line.tex`](examples/simple_line.tex).

## Long lines: the serpentine layout

When a line has too many stations to fit on a single row, enable **serpentine
wrapping**: set `max width` (the x, in cm, at which the line should wrap) and
optionally `row sep` (the vertical distance between rows). The cursor turns
automatically at the edge, drawing a nested U-turn, and you keep calling
`\addstation` as usual — no manual bookkeeping.

```latex
\begin{trainmap}[mode=rodalies, station step=1.6, max width=20, row sep=3.8,
                 badge color=tmR4]
  \settrunk{0/tmR4}
  \addterminal{Sant Vicenç de Calders}{R4}
  \addstation{El Vendrell}
  % ... many more stations ...
  \addterminal{Manresa}{R4}
\end{trainmap}
```

<p align="center">
  <img src="docs/preview_rodalies_r4.png" alt="The R4 line folded into three rows" width="92%">
</p>

Full source: [`examples/rodalies_r4.tex`](examples/rodalies_r4.tex) — the complete
R4 line (38 stations) in Rodalies style, folded over three rows.

For a bundle of several parallel tracks the U-turns nest concentrically, so the
lines stay parallel and never cross. Serpentine wrapping is most useful for one
or a few lines; give `row sep` enough room that the rotated labels of one row do
not reach the row above.

## Command reference

### The `trainmap` environment

```latex
\begin{trainmap}[<keys>] ... \end{trainmap}
```

Wraps a `tikzpicture`, fixes the drawing unit to 1 cm, and resets the internal
cursor and trunk state. Any `/trainmap/` key may be given in `<keys>` as a map
wide default. You can still add ordinary TikZ code (`\node`, `\draw`,
`\coordinate`, …) inside the environment, which is how the examples place
off-trunk terminals.

### Defining the trunk

```latex
\settrunk{<y>/<colour>, <y>/<colour>, ...}
```

Declares the parallel tracks that currently make up the common trunk. Each
entry is a vertical offset in centimetres and the colour of that track. The
tracks are drawn automatically between consecutive stations.

`\settrunk` may be **called again at any point** to change the active bundle —
for example after a line branches away it should be dropped from the trunk, or
a new line can be added. In the FGC example the trunk starts as four tracks and
is reduced to three and then two as L7 and L6 leave.

### Stations

| Command | What it draws |
|---------|---------------|
| `\addstation[<keys>]{<name>}` | A **standard station**: a perpendicular tick crossing every track of the trunk, plus a rotated label. |
| `\addterminal[<keys>]{<name>}{<line>}` | A **terminal / head station**: a filled disc in the badge colour and the name in bold above. In `fgc` mode a badge with `<line>` is drawn below the disc; in `rodalies` mode there is no per-terminal badge (the code appears on the end plates instead, and the first terminal's `<line>` becomes the line code if none was set). |
| `\addinterchange[<keys>]{<name>}` | An **interchange station**: a rounded capsule grouping all trunk tracks, with a coloured dot per line, and a bold label. |

Any `<name>` may contain `\\` to break it across several lines.

Every station call **advances the cursor** by `station step` centimetres (in
the current direction) before drawing, and publishes the reached position in the
macros `\tmlastx` and `\tmlasty`. This is how you anchor a branch to a station:
capture the value right after the call.

```latex
\addstation[name=gracia]{Gràcia}
\edef\gx{\tmlastx}          % \gx now holds Gràcia's x coordinate
```

Passing `name=<id>` additionally creates three reusable coordinates:
`(<id>)` at the top track, `(<id>-mid)` at the band centre, and `(<id>-bot)` at
the bottom track.

Useful per-station keys: `label position=below` (label under the track),
`station step=<cm>` (a custom gap before this one), `badge color=<colour>`
(for terminals and interchanges).

### Tracks, branches and forks

The `<from>` and `<to>` arguments are passed **with their parentheses**, so they
accept explicit coordinates, named nodes, or `calc` expressions
interchangeably:

```latex
\forkup{(\gx,0.36)}{(avtib)}{tmL7}          % coordinate  -> named node
\track{(sarria)}{($(sarria)+(2,0)$)}{tmL6}   % named node -> calc expression
```

| Command | What it draws |
|---------|---------------|
| `\track[<opts>]{<from>}{<to>}{<colour>}` | A straight track segment. |
| `\branch[<opts>]{<from>}{<to>}{<colour>}` | A gentle, near-horizontal curved branch. |
| `\forkup[<opts>]{<from>}{<to>}{<colour>}` | A branch that peels **upwards** (alias: `\forkline`). |
| `\forkdown[<opts>]{<from>}{<to>}{<colour>}` | A branch that peels **downwards**. |
| `\joinline[<opts>]{<from>}{<to>}{<colour>}` | A branch **merging back** into the trunk from below. |

`<opts>` is an optional list of raw TikZ options appended to the `\draw`
(for example `[looseness=1.2]` or `[dashed]`).

## Configuration keys

All keys live under `/trainmap/`. Set them on the environment for a map-wide
default, or in a command's `[ ... ]` to override a single element.

| Key | Default | Meaning |
|-----|---------|---------|
| `mode` | `fgc` | Map style: `fgc` (all lines, badges on terminals) or `rodalies` (single line, code on end plates). |
| `line code` | (empty) | Code shown on the end plates in `rodalies` mode. If empty, it is taken from the first `\addterminal`. |
| `line color` | `black` | Fallback colour of a single track. |
| `track width` | `2pt` | Stroke width of a track. |
| `station color` | `black` | Colour of the station tick mark. |
| `station width` | `1.8pt` | Stroke width of the tick mark. |
| `terminal size` | `8pt` | Diameter of the terminal disc. |
| `terminal border` | `1.4pt` | Border width of the terminal disc. |
| `badge color` | `black` | Background colour of the line badge. |
| `capsule width` | `12pt` | Outer width of the interchange capsule. |
| `capsule inner` | `8pt` | Inner (white) width of the capsule. |
| `capsule color` | `black` | Border colour of the capsule. |
| `station step` | `1.6` | Horizontal distance between stations (cm). |
| `track sep` | `0.16` | Suggested vertical gap between parallel tracks (cm). |
| `max width` | `0` | Wrap to a new row once the cursor passes this x (cm). `0` disables wrapping. |
| `row sep` | `2.6` | Vertical distance between wrapped rows (cm). |
| `label angle` | `45` | Rotation of the station labels (degrees). |
| `label position` | `above` | `above` or `below` — label over or under the track. |

## Predefined colours

The package defines the official line colours of **Rodalies de Catalunya** and
**FGC**; you can also use any `xcolor` colour such as `red` or `blue!60!black`.

**Rodalies de Catalunya**

| Name | Line | Name | Line | Name | Line |
|------|------|------|------|------|------|
| `tmR1` | R1 | `tmR8` | R8 | `tmR16` | R16 |
| `tmR2` | R2 | `tmR11` | R11 | `tmR17` | R17 |
| `tmR2N` | R2 Nord | `tmR12` | R12 | `tmRL1` | RL1 |
| `tmR2S` | R2 Sud | `tmR13` | R13 | `tmRL2` | RL2 |
| `tmR3` | R3 | `tmR14` | R14 | `tmRG1` | RG1 |
| `tmR4` | R4 | `tmR15` | R15 | `tmRT1` | RT1 |
| `tmR7` | R7 | | | `tmRT2` | RT2 |

**FGC** — Barcelona-Vallès: `tmL6`, `tmL7`, `tmL12`, `tmS1`, `tmS2`.
Llobregat-Anoia: `tmL8`, `tmR5`, `tmR50`, `tmR6`, `tmR60`.

### Correspondences

Any station accepts a `corr` key listing the connecting lines as
`CODE/colour` pairs; each becomes a small coloured chip stacked beneath the
station (as on the R7 map at the top of this page):

```latex
\addstation[corr={R3/tmR3, R4/tmR4}]{Montcada Bifurcació}
```

## How the engine works

`trainmap` keeps a small amount of state — a horizontal **cursor**, a travelling
**direction**, a **row** index and the list of trunk tracks. Each `\add...`
call advances the cursor, draws the trunk segment(s) leading up to it, and then
draws the station symbol and label. Coordinates are placed with `pgfmath`, so
the vertical positions of the tracks, the tick extents and the label anchors are
all derived from the keys rather than hard-coded.

For the serpentine layout the absolute y of a track stored as `l` is

```
y(l) = (rowbase + center) + sign * (l - center)
```

On the first row this reduces to `y = l`, so ordinary maps keep their absolute
coordinates (hand-placed branch endpoints remain valid). On a wrapped row the
`sign` flips, which mirrors the track order — exactly what a parallel U-turn
needs so the connecting half-loops nest concentrically instead of crossing.

## Repository layout

```
trainmap/
├── trainmap.sty                    # LaTeX wrapper
├── tikzlibrarytrainmap.code.tex    # Core TikZ library (the implementation)
├── trainmap.cwl                    # TeXstudio/TeXworks completion file
├── trainmap-doc.tex                # User manual (source)
├── trainmap.pdf                    # User manual (compiled)
├── examples/
│   ├── simple_line.tex             # Rodalies style: one line, end plates
│   ├── rodalies_r7.tex             # Rodalies style + correspondences (the R7 line)
│   ├── fgc_style_map.tex           # FGC style: common trunk, branches, interchange
│   └── rodalies_r4.tex             # Rodalies style + serpentine: the full R4 line
├── docs/
│   ├── preview_simple_line.png
│   ├── preview_rodalies_r7.png
│   ├── preview_fgc_style_map.png
│   └── preview_rodalies_r4.png
├── README.md
├── LICENSE                         # LaTeX Project Public License v1.3c
└── .gitignore
```

To build every example locally:

```bash
cd examples
pdflatex simple_line.tex
pdflatex rodalies_r7.tex
pdflatex fgc_style_map.tex
pdflatex rodalies_r4.tex
```

## License

Released under the **LaTeX Project Public License (LPPL), version 1.3c** or (at
your option) any later version — see [`LICENSE`](LICENSE). This is the standard
license for LaTeX packages and is the license under which `trainmap` is
distributed on CTAN. The package has LPPL maintenance status *maintained*; the
Current Maintainer is **Pol Sardà Palla**.
