Graphviz Layout: Node Spacing, Rank Spacing, and Direction

2020-02-15

This article is for people who are already familiar with Graphviz and want more control over the layout of a DOT diagram.

With the dot engine, set nodesep for the minimum space between nodes on the same rank and ranksep for the minimum space between ranks. rankdir=TB puts successive ranks below each other; rankdir=LR puts them to the right. These are layout requests, not fixed center-to-center distances.

Below are a few layout options for a small service diagram. For a focused example of putting nodes on the same rank, see how to align Graphviz nodes.


That would be our starting point, let's say we are creating a simple system architecture diagram.

digraph Graph {
  node[shape=rect]
  gateway -> users
  gateway -> companies
  gateway -> groups
}

graphviz_layout_initial open in playground

specify Width and Heigh of nodes

explicitly set width and height of nodes

digraph Graph {
  node[shape=rect]
  gateway[width=3 height=0.3]
  gateway -> users
  gateway -> companies
  gateway -> groups
}

graphviz_layout_node_size open in playground

splines - change arrow line shapes

digraph Graph {
  node[shape=rect]
  splines=ortho
  gateway[width=3 height=0.3]
  gateway -> users
  gateway -> companies
  gateway -> groups
}

graphviz_layout_arrow open in playground

Compare node and rank spacing with the same graph

In dot, both nodesep and ranksep are specified in inches. Defaults are nodesep=0.25 and ranksep=0.5. nodesep is the minimum gap between neighbors on one rank; ranksep is the minimum gap between ranks. Labels and other layout constraints can make the actual gaps larger.

Start with top-to-bottom (TB) flow, leaving both spacing values at their defaults:

digraph Service {
  graph [rankdir=TB];
  node [shape=box];
  request -> api;
  request -> worker;
  api -> store;
  worker -> store;
}

Open the default TB diagram in the playground

Default Graphviz TB layout showing Request above API and Worker, with Store below both

Now keep every node and edge but set nodesep=0.85 and ranksep=1.2:

digraph Service {
  graph [rankdir=TB, nodesep=0.85, ranksep=1.2];
  node [shape=box];
  request -> api;
  request -> worker;
  api -> store;
  worker -> store;
}

Open the spaced TB diagram in the playground

Spaced Graphviz TB layout with more horizontal space between API and Worker and more vertical space between ranks

In this four-node render, the API-to-Worker center distance grows from 73 to 116 points, and the Request-to-API rank distance grows from 72 to 122 points. Those measured distances describe this diagram, not a conversion rule from the attribute values: node sizes and routing affect the final geometry.

Rotate the same spaced graph from TB to LR

Change only rankdir to LR:

digraph Service {
  graph [rankdir=LR, nodesep=0.85, ranksep=1.2];
  node [shape=box];
  request -> api;
  request -> worker;
  api -> store;
  worker -> store;
}

Open the spaced LR diagram in the playground

Spaced Graphviz LR layout showing Request on the left, API above Worker in one column, and Store on the right

With TB, ranksep separates rows vertically and nodesep separates nodes across each row. With LR, ranksep separates columns horizontally and nodesep separates nodes vertically within a column. This comparison uses the site's Graphviz dot renderer; other engines such as neato do not follow the same rank rules. See the Graphviz nodesep and ranksep references for the full attribute behavior.

Now let's say I add one more edge, and it changes the weights of nodes and makes graph looks like that

digraph Graph {
  node[shape=rect]
  splines=ortho
  gateway[width=3 height=0.3]
  gateway -> users
  users -> companies
  gateway -> companies
  gateway -> groups
  nodesep=1
}

graphviz_layout_unranked there are 2 ways to address that

apply rank to subgraph items

you can apply same 'rank' to a subgraph

digraph Graph {
  node[shape=rect]
  splines=ortho
  gateway[width=3 height=0.3]
  gateway -> users
  users -> companies
  gateway -> companies
  gateway -> groups
  nodesep=1
  {
    rank=same
    users
    companies
  }
}

open playground graphviz_layout_ranked

constraint

if you see that some arrow adds to much weight to a node, you can exclude it from ranking

digraph Graph {
  node[shape=rect]
  splines=ortho
  gateway[width=3 height=0.3]
  gateway -> users
  users -> companies[constraint=false]
  gateway -> companies
  gateway -> groups
  nodesep=1
}

open playground graphviz_layout_ranked


Please checkout some of our useful resources:

  1. Graphviz cheatsheet
  2. Graphviz Playground
  3. Graphviz interactive tutorial
  4. Graphviz vs MermaidJS comparison
  5. Align Graphviz nodes on the same rank

This article was originally published in DevToolsDaily blog