4

I’ve got a rather complex node/edge setup with fixed positions for the nodes. Now I want the edges to be laid out “better”. Currently, it looks like this (Teχ code at the end):

current output

The things I want to improve are:

  • upper right corner: the connection from Admins to VerA.web-core should not be a straight-diagonal line but a straight line down then left then down, or left then down then left
  • upper left/middle: core→REST-API should be similar; core→OSIAM (bottom middle) can then start to the right of core→REST-API, loop around the very right of the drawing, and reach OSIAM from the tp
  • bottom: register-into-osiam.sh can loop below the other two to reach ong DB
  • etc.

I have the entire thing drawn out on my whiteboard crossing-free, and I know Graphviz could achieve it too, but I’m a beginner with TikZ/PGF and have no idea how to do it there, and, frankly, the documentation overwhelms me.

\begin{tikzpicture}[
  >=stealth',
  every node/.style={thick,text=black},
  every rectangle node/.style={above right,minimum height=10mm},
  apache/.style={rectangle,draw=Dandelion,minimum width=35mm},
  microsvc/.style={rectangle,draw=Orange,minimum width=40mm},
  webapp/.style={rectangle,draw=red,minimum width=40mm},
  syssvc/.style={rectangle,draw=Violet,minimum width=25mm},
  script/.style={rectangle,draw=black,minimum width=25mm},
  people/.style={rectangle,draw=CornflowerBlue,minimum width=25mm},
  acloud/.style={cloud,cloud puffs=9,draw=green,above right,minimum width=20mm,minimum height=10mm},
 ]

  \node[apache]   (alam)   at ( 90mm,85mm) {LDAP Account Mgr};
  \node[apache]   (acore)  at ( 90mm,70mm) {VerA.web-core};
  \node[apache]   (avwor)  at ( 90mm,55mm) {REST-API};
  \node[apache]   (avwoa)  at ( 90mm,40mm) {Online-Anmeldung};
  \node[apache]   (aosiam) at ( 90mm,10mm) {OSIAM} [minimum height=25mm];
  \node[microsvc] (svwoa)  at ( 40mm,40mm) {VWOA};
  \node[webapp]   (score)  at ( 40mm,70mm) {core};
  \node[webapp]   (svwor)  at ( 40mm,55mm) {VWOR};
  \node[webapp]   (sauth)  at ( 40mm,25mm) {auth-server};
  \node[webapp]   (srsrc)  at ( 40mm,10mm) {resource-server};
  \node[syssvc]   (ldap)   at (  5mm,85mm) {LDAP-Server};
  \node[syssvc]   (dbvw)   at (  5mm,65mm) {veraweb DB};
  \node[syssvc]   (dbong)  at (  5mm,20mm) {ong DB};
  \node[script]   (usql)   at (  5mm,50mm) {upgrade.sql};
  \node[script]   (riosh)  at (140mm,10mm) {register-into-osiam.sh};
  \node[people]   (admins) at (140mm,85mm) {Admins};
  \node[people]   (sb)     at (140mm,70mm) {Sachbearbeiter};
  \node[people]   (guests) at (158mm,40mm) {Gäste} [minimum width=15mm];
  \node[acloud]   (inet)   at (137mm,42mm) {};

  \draw[thick,->] (admins) -- (alam);
  \draw[thick,->] (alam) -- (ldap);
  \draw[thick,->] (admins) -- (acore);
  \draw[thick,->] (acore) -- (score);
  \draw[thick,->] (score) -- (ldap);
  \draw[thick,->] (sb) -- (acore);
  \draw[thick,->] (score) -- (dbvw);
  \draw[thick,->] (score) -- (aosiam);
  \draw[thick,->] (score) -- (avwor);
  \draw[thick,->] (guests) -- (inet);
  \draw[thick,->] (inet) -- (avwoa);
  \draw[thick,->] (avwoa) -- (svwoa);
  \draw[thick,->] (svwoa) -- (avwor);
  \draw[thick,->] (avwor) -- (svwor);
  \draw[thick,->] (svwor) -- (dbvw);
  \draw[thick,->] (usql) -- (dbvw);
  \draw[thick,->] (aosiam) -- (sauth);
  \draw[thick,->] (aosiam) -- (srsrc);
  \draw[thick,->] (sauth) -- (dbong);
  \draw[thick,->] (srsrc) -- (dbong);
  \draw[thick,->] (riosh) -- (dbong);
  \draw[thick,->] (sauth) -- (aosiam);
  \draw[thick,->] (srsrc) -- (aosiam);
  \draw[thick,->] (riosh) -- (aosiam);
  \draw[thick,->] (svwoa) -- (aosiam);

\end{tikzpicture}
  • what about \draw[options] (node A) -| (node B) or \draw[options] (node A) |- (node B) ? (if you can French, check §6.2.1 on : TikZ pour l'impatient ) – ebosi Dec 4 '15 at 16:32
  • 1
    @mirabilos You should provide a compilable MWE. – Arianna Angeletti Dec 4 '15 at 17:00
  • 2
    Until someone cooks up a solution, here are some similar questions (there are more ...): TikZ node placement and arrow drawing Connect Tikz Nodes running around nodes Vertical and horizontal lines in pgf-tikz Drawing path between nodes using TIKZ – Torbjørn T. Dec 4 '15 at 19:13
  • You should look into laying this out using the chains or matrix libraries or similar, rather than hard-coding the coordinates. If you need to alter a component's name, you will have to change the locations of enormous numbers of nodes unless it is the same length as the current one. – cfr Dec 6 '15 at 4:36
  • I would also recommend either increasing the vertical spacing, or altering the use of arrow tips as they are too squashed this way. You could mark these on the lines rather than at the ends, for example, and use small circles as a kind of 'port' for the lines to enter and leave the nodes. This actually gives you multiple joining points as you can join bang centre or to the left or right, which keeps multiple line entry/exit neat. (I have some experience with this: see here but you probably need to follow the link to the PDF to see the circles.) – cfr Dec 6 '15 at 4:44
7

Here is a solution:

\documentclass[landscape]{article}
\usepackage[dvipsnames]{xcolor}
\usepackage{tikz}
\usetikzlibrary{arrows,shapes}


\begin{document}

\begin{tikzpicture}[
  >=stealth',
  every node/.style={thick,text=black},
  every rectangle node/.style={above right,minimum height=10mm},
  apache/.style={rectangle,draw=Dandelion,minimum width=35mm},
  microsvc/.style={rectangle,draw=Orange,minimum width=40mm},
  webapp/.style={rectangle,draw=red,minimum width=40mm},
  syssvc/.style={rectangle,draw=Violet,minimum width=25mm},
  script/.style={rectangle,draw=black,minimum width=25mm},
  people/.style={rectangle,draw=CornflowerBlue,minimum width=25mm},
  acloud/.style={cloud,cloud puffs=9,draw=green,above right,minimum width=20mm,minimum height=10mm},
 ]

  \node[apache]   (alam)   at ( 90mm,85mm) {LDAP Account Mgr};
  \node[apache]   (acore)  at ( 90mm,70mm) {VerA.web-core};
  \node[apache]   (avwor)  at ( 90mm,55mm) {REST-API};
  \node[apache]   (avwoa)  at ( 90mm,40mm) {Online-Anmeldung};
  \node[apache]   (aosiam) at ( 90mm,10mm) {OSIAM} [minimum height=25mm];
  \node[microsvc] (svwoa)  at ( 40mm,40mm) {VWOA};
  \node[webapp]   (score)  at ( 40mm,70mm) {core};
  \node[webapp]   (svwor)  at ( 40mm,55mm) {VWOR};
  \node[webapp]   (sauth)  at ( 40mm,25mm) {auth-server};
  \node[webapp]   (srsrc)  at ( 40mm,10mm) {resource-server};
  \node[syssvc]   (ldap)   at (  5mm,85mm) {LDAP-Server};
  \node[syssvc]   (dbvw)   at (  5mm,65mm) {veraweb DB};
  \node[syssvc]   (dbong)  at (  5mm,20mm) {ong DB};
  \node[script]   (usql)   at (  5mm,50mm) {upgrade.sql};
  \node[script]   (riosh)  at (140mm,10mm) {register-into-osiam.sh};
  \node[people]   (admins) at (140mm,85mm) {Admins};
  \node[people]   (sb)     at (140mm,70mm) {Sachbearbeiter};
  \node[people]   (guests) at (158mm,40mm) {G\"aste} [minimum width=15mm];
  \node[acloud]   (inet)   at (137mm,42mm) {};

  \draw[thick,->] (admins) -- (alam);
  \draw[thick,->] (alam) -- (ldap);
  \draw[thick,->] (admins) |- +(-2cm,-7.5mm) -| (acore);
  \draw[thick,->] (acore) -- (score);
  \draw[thick,->] (score) |- +(-2.5cm,7.5mm) -| (ldap);
  \draw[thick,->] (sb) -- (acore);
  \draw[thick,->] (score) -| +(-2.5cm,3mm) -| (dbvw);
  \draw[thick,->] ([xshift=4mm]score) |- ++(12.2cm,-6mm) |- ++(-2cm,-4cm) -| ([xshift=4mm]aosiam);
  \draw[thick,->] (score) |- +(2.5cm,-7.5mm) -| (avwor);
  \draw[thick,->] (guests) -- (inet);
  \draw[thick,->] (inet) -- (avwoa);
  \draw[thick,->] (avwoa) -- (svwoa);
  \draw[thick,->] (svwoa) |- +(2.5cm,7.5mm) -| (avwor);
  \draw[thick,->] (avwor) -- (svwor);
  \draw[thick,->] (svwor) -| +(-2.5cm,5mm) |- (dbvw);
  \draw[thick,->] (usql) -- (dbvw);
  \draw[thick,<->] ([xshift=-4mm]aosiam) |- +(-2.5cm,7.5mm) -| (sauth);
  \draw[thick,<->] (aosiam) -- (srsrc);
  \draw[thick,->] (sauth) -| +(-2.5cm,3mm) -| (dbong);
  \draw[thick,->] (srsrc) -| ([xshift=2mm]dbong);
  \draw[thick,->] (riosh) -- +(0mm,-7.5mm) -| ([xshift=-2mm]dbong);
  \draw[thick,->] (riosh) -- (aosiam);
  \draw[thick,->] (svwoa) |- +(2.5cm,-7.5mm) -| (aosiam);

\end{tikzpicture}

\end{document}

It basically defines reference points by moving from the respective start points using the +(x,y) coordinates and connects them using the |- and -| connections. In case of the more complex line around the whole picture, we need two reference points and therefore need to use ++ instead of + such that further moving is relative to the last reference point. The offsets for the start/end points of the arrows are achieved by xshifts. The output looks as follows:

Output of code above

5

Here's a way of using chains to construct the diagram somewhat more flexibly.

  • I've increased the vertical spacing a bit so that the arrows do not look squished.
  • I've also switched to the current arrows.meta library, replacing the deprecated syntax.
  • There is some commented code in the middle which adds the names of the nodes within the chains in red for ease of reference.

nodes in construction

  • Some of the connections are made using the join facilities from the chains library.
  • The more complex lines are added after the chains are constructed.
  • Three styles are used for the connections, whether constructed as part of the chains or not: tarr, rarr and darr for a thick arrow, a thick reversed arrow and a thick double arrow.
  • Most nodes are on a single chain called initial. This has a few branches: initial/first, initial/first/second and initial/third.
  • For the nodes on the left, which are not aligned with the others, the nodes are first drawn and then added to the relevant branch.

finalised diagram

If you would like to add fancy shadows, add shadows to the list of libraries and change

  every node/.style={thick,text=black,minimum height=10mm},

to

  every node/.style={thick,text=black,minimum height=10mm, fill=white, drop shadow},

to produce

fancy shadows

Code:

\documentclass[tikz,border=10pt,multi,x11names,dvipsnames,svgnames]{standalone}
\usetikzlibrary{shapes.symbols,arrows.meta,positioning,chains,scopes}
\usepackage[utf8]{inputenc}
\begin{document}
\begin{tikzpicture}[
  >={Stealth[]},
  tarr/.style={thick, ->},
  rarr/.style={thick, <-},
  darr/.style={thick, <->},
  every node/.style={thick,text=black,minimum height=10mm},
  apache/.style={draw=Dandelion,minimum width=35mm},
  microsvc/.style={draw=Orange,minimum width=40mm},
  webapp/.style={draw=red,minimum width=40mm},
  syssvc/.style={draw=Violet,minimum width=25mm},
  script/.style={draw=black,minimum width=25mm},
  people/.style={draw=CornflowerBlue,minimum width=25mm},
  acloud/.style={cloud,cloud puffs=9,draw=green,minimum width=20mm},
 ]
 {[start chain=initial going left]
   \foreach \i/\j in {register-into-osiam.sh/script,OSIAM/apache}
   \node [on chain, \j, join=by tarr] {\i};
   \node [on chain, webapp, join=by darr] {resource-server};
   {[continue chain=going above]
     \foreach \i/\j in {auth-server/webapp,VWOA/microsvc,VWOR/webapp}
     \node [on chain, \j] {\i};
     \node [on chain, webapp] {core};
   }
   {[continue chain=going right]
      \node [on chain, apache, join=by rarr] {VerA.web-core};
      {[start branch=first going above]
        \node [on chain, apache] {LDAP Account Mgr};
        {[start branch=second going left]
        }
        {[continue chain=going right]
          \node [on chain, people, join=by rarr] {Admins};
        }
      }
      {[start branch=third going right]
        \node [on chain, people, join=by rarr] {Sachbearbeiter};
      }
   }
   {[continue chain=going below]
      \node [on chain, apache, join=with initial-6 by rarr] {REST-API};
      \node [on chain, apache, join=with initial-5 by rarr] {Online-Anmeldung};
   }
   {[continue chain=going right]
      \node [on chain, acloud, join=by rarr] {};
      \node [on chain, people, join=by rarr] {Gäste};
   }
   \node (ldap server) [syssvc, left=of initial/first-2.center -| initial-6.west] {LDAP-Server};
   \node (veraweb db) [below=2 of ldap server, syssvc] {veraweb DB};
   \node (ong db) [above=1 of initial-3.south -| veraweb db, syssvc] {ong DB};
   {[continue branch=first/second going below]
     \chainin (ldap server) [join=by tarr];
     \chainin (veraweb db);
     \node [on chain, script, join=by rarr] {upgrade.sql};
     \chainin (ong db);
   }
 }
 % for viewing the labels easily during construction
%  \foreach \i in {1,...,12} \node [red] at ([yshift=-10pt]initial-\i) {initial-\i};
%  \foreach \i in {2,3} \node [red] at ([yshift=-10pt]initial/first-\i) {initial/first-\i};
%  \foreach \i in {2,...,5} \node [red] at ([yshift=-10pt]initial/first/second-\i) {initial/first/second-\i};
%  \foreach \i in {2} \node [red] at ([yshift=-10pt]initial/third-\i) {initial/third-\i};
 \foreach \i/\j in {initial-3.west/ong db.south, initial-4.west/ong db.north, initial-7.west/veraweb db.north} \draw  [tarr] (\i) -| (\j);
 \foreach \i/\j in {initial-6.north/veraweb db.east} \draw [tarr] (\i) |- (\j);
 \draw [tarr] (initial-7.north) -- +(0,.5) -| (ldap server.south);
 \draw [tarr] ([xshift=20pt]initial-7.south) -- +(0,-.65) -| (initial-9.north);
 \draw [darr] (initial-4.east) -| ([xshift=-20pt]initial-2.north);
 \foreach \i/\j/\k in {initial-5.south/initial-2.north/0,initial-1.south/ong db.south/-20pt,{initial/first-3.south}/initial-8.north/0}
 \draw [tarr] (\i) -- +(0,-.5) -| ([xshift=\k]\j);
 \draw [tarr] (initial-5.north) -- +(0,.5) -| (initial-9.south);
 \coordinate [xshift=5mm,yshift=5mm] (c) at (initial-12.east |- initial-1.north);
 \draw [tarr] ([xshift=40pt]initial-7.south) -- +(0,-.35) -| (c) -| ([xshift=20pt]initial-2.north);
\end{tikzpicture}
\end{document}
  • Thanks, this is amazing, but I think I’ll go with the other answer because this is easily way above my head for now, and I’ll need to do modifications and a couple of those. Nevertheless, much appreciated, and probably a good reference answer for others attempting the same, and something I will study in detail later when I’m not on a time deadline. – mirabilos Dec 7 '15 at 11:10
  • @mirabilos Definitely a deadline is not the time to experiment! Possibly it will be of interest later. In general, if you can avoid hard-coding absolute node positions, it tends to be easier to adjust a diagram later if need be. – cfr Dec 7 '15 at 12:02
  • Yeah, I had thought of using Graphviz (which I know quite a bit better, though by no means an expert there either), but I wanted to use the columns for clustering (e.g. the third columns is all Apache) here, i.e. assign functional meaning to the node positions, which kinda ruled out Graphviz mostly, plus why not learn the Teχ way of doing things when I’m trying to learn and push Teχ anyway (though I’m still more of an nroff person, at least for my private OSS projects). – mirabilos Dec 7 '15 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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