# Tikz: Drawing railway tracks

I like to draw a double line such that the gap between the two lines is transparent. My purpose is to draw railway tracks:

 \documentclass[tikz]{standalone}

\tikzstyle{track}=[
postaction={draw=gray,densely dashed,line width=14pt},
postaction={draw=black,double distance=8pt,line width=2pt},
postaction={draw=gray,densely dashed,line width=8pt},]

\begin{document}

\begin{tikzpicture}

\draw[track] (-5,27) to[out=  0,in=180] ( 5,33)
to[out=  0,in= 90] ( 7,25)
to[out=270,in=  0] (-5,27);

\draw[track] (-4,10) to (0,10) to[out=  0,in=270] (4,16);
\draw[track] (12,10) to (8,10) to[out=180,in=270] (4,16) to (4,20);

\draw[track] (-4,0) to (12,0);

\draw[track] (0,0) to[out=  0,in=270] (3,2)
to[out= 90,in=270] (1,4)
to[out= 90,in=180] (4,6)
to[out=  0,in=120] (8,5)
to[out=300,in=  0] (4,0);
\end{tikzpicture}

\end{document}


The second postaction draws a double line, but the gap between the two lines is not transparent. Therefore, I made the third postaction. But I expect that the tracks will look much more realistic (in particular at the switches) if the gap of the double lines is transparent. Any ideas?

-
Good question, and a really clever way to draw the sleepers! –  Jake Aug 26 '14 at 20:17
double distance (or simply double) draws the path twice---one with the base color, and one with the inbetween color. If you specify double=transparent, so that the inbetween color is transparent, you will see that instead of getting a white fill, it is all solid black. If you want a different solution than your own, double distance might not be the appropriate command. A different approach could be to instead draw two black lines---one shifted to the left and one shifted to the right. –  eiterorm Aug 26 '14 at 20:26
I tried the solution of tex.stackexchange.com/questions/102975/… without success: The decorations in the postactions were not drawn along the path. –  user61383 Aug 26 '14 at 20:39
The first try of tex.stackexchange.com/questions/193243/… seems to be quite close, but I could not make it working. The other solutions there use knots or double which have the same non-transparent issue. –  user61383 Aug 26 '14 at 20:44

This can be done (admittedly with some effort) using fadings and decorations. The following is I guess more proof-of-concept than anything else but might prove useful:

\documentclass[tikz, border=5]{standalone}

% Layers
\pgfdeclarelayer{sleeper}
\pgfdeclarelayer{rail}
\pgfsetlayers{sleeper,rail,main}

\pgfdeclaredecoration{tracks}{final}{%
\state{final}{%
\pgftransformreset% <- I think is possibly vital.
%
% Get bounding box of decorated path as a node.
% Must do it this way using basic layer.
\pgftransformshift{\pgfpointanchor{current path bounding box}{south west}}%
\pgfcoordinate{@1}\pgfpointorigin%
\pgftransformshift{\pgfpointanchor{current path bounding box}{north east}}%
\pgfcoordinate{@2}\pgfpointorigin%
\node [fit=(@1)(@2), inner sep=\railsep+2*\railwidth] (@@) {};
%
% Create a fading for the track.
\pgfinterruptpicture%
\path[draw=transparent!0, line width=\railsep+2*\railwidth,
postaction={draw=transparent!100, line width=\railsep}]
\pgfextra{\pgfsetpath\pgfdecoratedpath};
\useasboundingbox (@@.south west) (@@.north east);
\endpgfinterruptpicture
%
% Draw sleepers.
\ifx\sleeperlayer\emptylayer\else\pgfonlayer{\sleeperlayer}\fi%
\draw [draw=\sleepercolor,line width=\sleeperlength, dash pattern=on \sleeperwidth off \sleepersep, every sleeper/.try]
\pgfextra{\pgfsetpath\pgfdecoratedpath};
\ifx\sleeperlayer\emptylayer\else\endpgfonlayer\fi%
%
% Draw the track
\ifx\raillayer\emptylayer\else\pgfonlayer{\raillayer}\fi%
(@@.south west) rectangle (@@.north east);
\ifx\raillayer\emptylayer\else\endpgfonlayer\fi%
}
}
\def\emptylayer{}
\tikzset{%
track/.style={
decoration=tracks, decorate
},
decorations/.cd,
rail sep/.store in=\railsep,
rail width/.store in=\railwidth,
rail color/.store in=\railcolor,
rail layer/.store in=\raillayer,
sleeper sep/.store in=\sleepersep,
sleeper width/.store in=\sleeperwidth,
sleeper length/.store in=\sleeperlength,
sleeper color/.store in=\sleepercolor,
sleeper layer/.store in=\sleeperlayer,
rail sep=4pt,
rail width=1pt,
rail color=black,
rail layer=rail,
sleeper sep=6pt,
sleeper width=1pt,
sleeper length=10pt,
sleeper color=gray,
sleeper layer=sleeper,
}
\begin{document}
\begin{tikzpicture}
\draw [track] (-2,5) to (0,5) to[out=  0,in=270] (2,8);
\draw [track] (6,5) to (4,5) to[out=180,in=270] (2,8) to (2,10);
\end{tikzpicture}
\end{document}


-
Remark: If you wanted to add the background, you can, there are z-layers in TikZ, it's a bit complicated (you need to scope everything) but it works. –  yo' Aug 27 '14 at 7:54
This solves my problem. Additionally, I will split the decoration into two such that I can draw all sleepers at a layer in a first step and then draw all rails on another layer--like tracks are build in practice. –  user61383 Aug 27 '14 at 7:55
@user61383 you don't necessarily have to split the decoration as the the decoration can use different layers. See the updated answer –  Mark Wibrow Aug 27 '14 at 8:53

I think this is the best suggestion I have:

\documentclass[tikz]{standalone}
\usetikzlibrary{decorations.pathmorphing}

\tikzstyle{track}=[
postaction={draw=gray,densely dashed,line width=14pt},
postaction={draw,decorate,decoration={curveto,raise=4pt},line width=2pt},
postaction={draw,decorate,decoration={curveto,raise=-4pt},line width=2pt}]

\begin{document}

\begin{tikzpicture}

\path[track] (-5,27)    to[out=  0,in=180] ( 5,33)
to[out=  0,in= 90] ( 7,25)
to[out=270,in=  0] (-5,27);

\path[track] (-4,10)    to ( 0,10) to[out=  0,in=270] (4,16);
\path[track] (12,10)    to ( 8,10) to[out=180,in=270] (4,16) to (4,20);

\path[track] (-4, 0)    to (12, 0);

\path[track] ( 0, 0)    to[out=  0,in=270] (3,2)
to[out= 90,in=270] (1,4)
to[out= 90,in=180] (4,6)
to[out=  0,in=120] (8,5)
to[out=300,in=  0] (4,0);
\end{tikzpicture}

\end{document}


As you can see, this creates a problem at the endpoints, where the rails meet at the center line. By defining a custom decoration (somewhat beyond my experience), you might be able to also make the curveto line start at a raised point. If not, a crude workaround would be to erase the endpoints by drawing a white rectangle on top of them.

-
You need to draw the sleepers in a first pass, then the rails in a second pass. –  Nicholas Aug 27 '14 at 13:28