Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm looking for a simple way to draw the following picture using TikZ. Namely, I would like to only specify the coordinates of the cities and then to connect them by edges using a \foreach loop. This would be easy if there were no directed edges at this picture:

enter image description here

So, more precisely, my question is how to modify the second \foreach below to get a picture above.

\begin{scope}
\foreach \x/\y/\name/\label/\where in {1.2/1.4/a/{El Paso}/below,     
   1/3.2/b/Albuquerque/above, 2.5/3/c/Amarillo/below, 3.8/4/d/Wichita/above,  
   4.3/3.3/e/Tulsa/45, 5.2/2.8/f/{Little Rock}/right, 4.2/2/g/Dallas/left, 
   4.8/1/h/Houston/right, 3.6/0.6/i/{San Antonio}/below} {
  \draw (\x,\y) circle (.4mm);
\node[draw,circle,minimum size=2mm,inner sep=0mm,label=\where:{\footnotesize \label}] (\name) at (\x,\y) {};
}

\foreach \s/\t in {a/b, b/c, c/e, e/d, e/f, f/g, h/g, h/i}
  \path[draw] (\s) edge (\t);
\end{scope}
share|improve this question
1  
Welcome to TeX.SX. –  Claudio Fiandrino Dec 5 '12 at 17:02

3 Answers 3

up vote 11 down vote accepted

If you create a simple "decoration" which simply draws a stroke from the initial to the final point, you can use the raise option for decorated paths to get a parallel path. Applying shorten > and shorten < to this raised path, and putting arrow tips on it, you get the desired result:

Result

Here is the code (the style parallel arrow takes care of all):

\documentclass{article}
\usepackage{tikz}
\begin{document}
\usetikzlibrary{decorations}
\pgfdeclaredecoration{sl}{initial}{
  \state{initial}[width=\pgfdecoratedpathlength-1sp]{
     \pgfmoveto{\pgfpointorigin}
  }
  \state{final}{
     \pgflineto{\pgfpointorigin}
    }
}

\tikzset{parallel arrow/.style={->, 
     shorten >=2mm, shorten <=2mm, 
     decoration={sl,raise=1mm},decorate}}

\begin{tikzpicture}
\begin{scope}
\foreach \x/\y/\name/\label/\where in {1.2/1.4/a/{El Paso}/below,     
   1/3.2/b/Albuquerque/above, 2.5/3/c/Amarillo/below, 3.8/4/d/Wichita/above,  
   4.3/3.3/e/Tulsa/45, 5.2/2.8/f/{Little Rock}/right, 4.2/2/g/Dallas/left, 
   4.8/1/h/Houston/right, 3.6/0.6/i/{San Antonio}/below} {
  \draw (\x,\y) circle (.4mm);
\node[draw,circle,minimum size=2mm,inner sep=0mm,label=\where:{\footnotesize \label}] (\name) at (\x,\y) {};
}

\foreach \s/\t in {a/b, b/c, c/e, e/d, e/f, f/g, h/g, h/i} {
  \path[draw] (\s) edge (\t);
  \path[draw] (\s) edge[parallel arrow] (\t);
  \path[draw] (\t) edge[parallel arrow] (\s);}
\end{scope}
\end{tikzpicture}
\end{document}

Update

I added the numbers in each route. I had some fun parametrizing their position so that they can be all put in a single loop.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations,calc}
\begin{document}
\pgfdeclaredecoration{sl}{initial}{
  \state{initial}[width=\pgfdecoratedpathlength-1sp]{
     \pgfmoveto{\pgfpointorigin}
  }
  \state{final}{
     \pgflineto{\pgfpointorigin}
    }
}
\tikzset{parallel arrow/.style={latex-,
     shorten >=2mm, shorten <=2mm, 
     decoration={sl,raise=1mm},decorate}}

\begin{tikzpicture}
\begin{scope}
\foreach \x/\y/\name/\label/\where in {1.2/1.4/a/{El Paso}/below,     
   1/3.2/b/Albuquerque/above, 2.5/3/c/Amarillo/below, 3.8/4/d/Wichita/above,  
   4.3/3.3/e/Tulsa/45, 5.2/2.8/f/{Little Rock}/right, 4.2/2/g/Dallas/left, 
   4.8/1/h/Houston/right, 3.6/0.6/i/{San Antonio}/below} {
  \draw (\x,\y) circle (.4mm);
\node[draw,circle,minimum size=2mm,inner sep=0mm,label=\where:{\footnotesize \label}] (\name) at (\x,\y) {};
}

\tikzset{edge label/.style={font=\tiny, inner sep=1.6mm},
ab/.style={edge label, left},
ba/.style={edge label, right},
bc/.style={edge label, above},
cb/.style={edge label, below},
ce/.style={bc},
ec/.style={cb},
ed/.style={ab},
de/.style={ba},
ef/.style={edge label, below left, inner sep=1mm},
fe/.style={edge label, above right, inner sep=1mm},
fg/.style={edge label, above left, inner sep=1mm},
gf/.style={edge label, below right, inner sep=1mm},
hg/.style={ab},
gh/.style={ba},
hi/.style={bc},
ih/.style={cb}
}  

\foreach \s/\t/\from/\to in {a/b/9/10, b/c/8/11, c/e/7/12, e/d/6/5, 
                             e/f/13/4, f/g/14/3, h/g/15/2, h/i/16/1} {
  \path[draw] (\s) edge (\t) ($(\s)!.5!(\t)$) node[\s\t] {\from} node[\t\s]{\to};
  \path[draw] (\s) edge[parallel arrow] (\t);
  \path[draw] (\t) edge[parallel arrow] (\s);
}
\end{scope}
\end{tikzpicture}
\end{document}

Result

share|improve this answer
    
Thank you very much, @JLDiaz! This is exactly what I was looking for. Now I should learn these decorations. =) –  Alexander S. Kulikov Dec 7 '12 at 9:53

JLDiaz's answer is excellent, this is just a small addition to it:

I wanted to avoid hard coding the values for the directed edges, so I thought I'd use the auto placement option, which places nodes to the side of a path. However, it turns out that this only happens in 45 degree steps: Nodes are either placed using their south anchor, or their north west anchor, etc., but no intermediate values. For this application, that's not precise enough. However, if you redefine two internal functions, you can get degree-accuracy placement of the nodes:

\makeatletter
\def\tikz@auto@anchor{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)-90}
    \edef\tikz@anchor{\angle}%
}

\def\tikz@auto@anchor@prime{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)+90}
    \edef\tikz@anchor{\angle}%
}
\makeatother

To get an even offset of the labels from the centre line, I used an ellipse node shape.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations,calc, shapes.geometric}

\makeatletter
\def\tikz@auto@anchor{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)-90}
    \edef\tikz@anchor{\angle}%
}

\def\tikz@auto@anchor@prime{%
    \pgfmathtruncatemacro\angle{atan2(\pgf@x,\pgf@y)+90}
    \edef\tikz@anchor{\angle}%
}
\makeatother

\begin{document}
\pgfdeclaredecoration{sl}{initial}{
  \state{initial}[width=\pgfdecoratedpathlength-1sp]{
     \pgfmoveto{\pgfpointorigin}
  }
  \state{final}{
     \pgflineto{\pgfpointorigin}
    }
}
\tikzset{parallel arrow/.style={latex-,
     shorten >=2mm, shorten <=1mm, 
     decoration={sl,raise=1mm},decorate}}

\begin{tikzpicture}
\begin{scope}
\foreach \x/\y/\name/\label/\where in {1.2/1.4/a/{El Paso}/below,     
   1/3.2/b/Albuquerque/above, 2.5/3/c/Amarillo/below, 3.8/4/d/Wichita/above,  
   4.3/3.3/e/Tulsa/30, 5.2/2.8/f/{Little Rock}/right, 4.2/2/g/Dallas/left, 
   4.8/1/h/Houston/right, 3.6/0.6/i/{San Antonio}/below} {
  \draw (\x,\y) circle (.4mm);
\node[draw,circle,minimum size=2mm,inner sep=0mm,label=\where:{\footnotesize \label}] (\name) at (\x,\y) {};
}

\tikzset{
    edge label/.style={
        font=\tiny,
        auto=right,
        ellipse,inner sep=1mm,
    }
}  

\foreach \s/\t/\from/\to in {a/b/9/10, b/c/8/11, c/e/7/12, e/d/6/5, 
                             e/f/13/4, f/g/14/3, h/g/15/2, h/i/16/1} {
  \path[draw] (\s) edge (\t);
  \path[draw] (\s) edge[parallel arrow]
    node [edge label] {\from} (\t);
  \path[draw] (\t) edge[parallel arrow]
  node [edge label] {\to}(\s);
}
\end{scope}
\end{tikzpicture}
\end{document}
share|improve this answer
    
Thank you, @Jake! It is nice to have an alternative way to draw this. –  Alexander S. Kulikov Dec 7 '12 at 9:55

Here is another method using to path and calc library.

An edge and its parallel arrows with labels are drawn by a single to operation:

\draw (from) to[edge with values=XX and YY] (to);

(where XX and YY are from->to and to->from labels. They are automatically positioned.) enter image description here

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{
  % store of parameters
  parallel distance/.store in=\paradist,
  parallel shorten/.store in=\parashorten,
  parallel label distance/.store in=\paralabdist,
  % default values
  parallel distance=1mm, % distance of parallel arrows from link
  parallel shorten=3mm, % to shorten parallel arrows
  parallel label distance=2.5mm, % distance of label from link
  % main style
  edge with values/.style args={#1 and #2}{
    to path={
      \pgfextra{
        \pgfinterruptpath
        % middle point
        \coordinate (m) at ($(\tikztostart)!.5!(\tikztotarget)$);
        % from-to arrow
        \draw[-stealth,shorten >=\parashorten,shorten <=\parashorten]
        ($(\tikztostart)!\paradist!-90:(\tikztotarget)$)
        --
        ($(\tikztotarget)!\paradist!90:(\tikztostart)$)
        % middle of from-to arrow
        coordinate[pos=.5](m from);
        % label of from-to arrow
        \node[font=\tiny] at ($(m)!\paralabdist!(m from)$){#1};
        % to-from arrow
        \draw[-stealth,shorten >=\parashorten,shorten <=\parashorten]
        ($(\tikztotarget)!\paradist!-90:(\tikztostart)$)
        --
        ($(\tikztostart)!\paradist!90:(\tikztotarget)$)
        % middle of to-from arrow
        coordinate[pos=.5](m to);
        % label of to-from arrow
        \node[font=\tiny] at ($(m)!\paralabdist!(m to)$){#2};
        \endpgfinterruptpath
      }
      % link
      (\tikztostart) -- (\tikztotarget)
    },
  },
}

\begin{document}
\begin{tikzpicture}
  \begin{scope}
    \foreach \x/\y/\name/\label/\where in {%
      1.2/1.4/a/{El Paso}/below,%     
      1/3.2/b/Albuquerque/above,%
      2.5/3/c/Amarillo/below,%
      3.8/4/d/Wichita/above,%
      4.3/3.3/e/Tulsa/45,%
      5.2/2.8/f/{Little Rock}/right,%
      4.2/2/g/Dallas/left,%
      4.8/1/h/Houston/right,%
      3.6/0.6/i/{San Antonio}/below%
    } {
      \draw (\x,\y) circle (.4mm);
      \node[draw,circle,minimum size=2mm,inner sep=0mm,
      label=\where:{\footnotesize \label}]
      (\name) at (\x,\y) {};
    }

    \foreach \s/\t/\from/\to in {
      a/b/10/9,
      b/c/11/8,
      c/e/12/7,
      e/d/5/6, 
      e/f/13/4,
      f/g/14/3,
      h/g/2/15,
      h/i/16/1
    } {
      \path[draw] (\s) to[edge with values=\from{} and \to] (\t);
    }
  \end{scope}
\end{tikzpicture}
\end{document}
share|improve this answer
    
Ah, very elegant! –  Jake Dec 5 '12 at 22:23
    
@Jake: Thanks... –  Paul Gaborit Dec 5 '12 at 22:25
    
Indeed! =) Thank you, Paul! –  Alexander S. Kulikov Dec 7 '12 at 9:56

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.