Graphviz: Changing node spacing within a subgraph cluster - graphviz

Experienced but no expert in Graphviz.
The example code and image that I am sharing is part of a larger graph, I pulled this out to make the question and example clearer.
The graph is rank separated and is flowing from left to right. In the image example you see three of the ranks and nodes space nicely but I want to tighten them in the subgraph.
I have tried a half dozen syntactical options but none of them allow me to shrink the vertical distance between nodes in the subgraph and no where else.
Not sure how to isolate the nodesep to just the subgraph cluster.
Thank you for any help.
digraph G {
graph [rankdir=LR, ranksep=.8, nodesep=.25];
edge [dir=forward, color=black];
node [shape=rectangle, fontsize=12, fontname="Times-Roman", height=.45];
{edge [color=firebrick1] v_Automobile ->{"Locational"; "Consumption"; "Governmental"};};
{edge [color=blue2] v_Body ->{"BioMetric"; "Networked IoT"; "Medical"};};
{edge [color=darkviolet] v_Citizen ->{"Associative"; "Conversaional"; "Governmental"; "Political"};};
{edge [color=lightskyblue] v_Consumer ->{"Consumption"; "Educational"; "Employment"; "Governmental"; "Locational"; "Medical"; "Transactional"};};
{edge [color=crimson] v_Home ->{"Consumption"; "Emanative"; "Locational"; "Networked IoT"};};
{edge [color=blue2] "BioMetric" ->{SP_Camera; SP_Apps; "CCTV"};};
{edge [color=crimson] "Consumption" ->"Smart Meter"};
{edge [color=blue2] "Medical" ->SP_Apps};
{edge [color=darkviolet] "Political" ->"Ballot"};
{edge [color=lightskyblue] "Transactional" ->{SP_Browser; SP_Apps; "Reward Cards"; "Paypoint"};};
subgraph cluster_SmartPhone {label = "Smart Phone";
dirType=none;
SP_Apps;
SP_Browser;
SP_Camera;
SP_GPS};
SP_Apps [label="Apps",
height=.40,
color=black];
SP_Browser [label="Browser",
color=black];
SP_Camera [label="Camera",
color=black];
SP_GPS [label="GPS",
color=black];
v_Automobile [label="Automobile",
penwidth=2,
color=firebrick1];
v_Body [label="Body",
penwidth=2,
color=blue2];
v_Citizen [label="Citizen",
penwidth=2,
color=darkviolet];
v_Consumer [label="Consumer",
penwidth=2,
color=lightskyblue];
v_Home [label="Home",
penwidth=2,
color=crimson];
}

If you are sure that these nodes you want to tighten are on the same rank, you may replace them with a table. In table you can remove the outer border and set the cellspacing attribute to control distance between cells.
To make edges to these pseudo-nodes you will have to set the port attribute to each cell and then reference these ports as headport attribute or node:port syntax.
Your only inconvenience would be that you can't make edges to multiple nodes containing these pseudonodes with syntax node1 -> {pseudonode1;pseudonode2;node3} because headport is the atribute of the edge. So you'll have to state these edges separately.
Your example with this technique will look like this:
digraph G {
graph [rankdir=LR, ranksep=.8, nodesep=.25];
edge [dir=forward, color=black];
node [shape=rectangle, fontsize=12, fontname="Times-Roman", height=.45];
{edge [color=firebrick1] v_Automobile ->{"Locational"; "Consumption"; "Governmental"};};
{edge [color=blue2] v_Body ->{"BioMetric"; "Networked IoT"; "Medical"};};
{edge [color=darkviolet] v_Citizen ->{"Associative"; "Conversaional"; "Governmental"; "Political"};};
{edge [color=lightskyblue] v_Consumer ->{"Consumption"; "Educational"; "Employment"; "Governmental"; "Locational"; "Medical"; "Transactional"};};
{edge [color=crimson] v_Home ->{"Consumption"; "Emanative"; "Locational"; "Networked IoT"};};
{edge [color=blue2] "BioMetric" -> tight:Camera;};
{edge [color=blue2] "BioMetric" -> tight:Apps;};
{edge [color=blue2] "BioMetric" ->{"CCTV"};};
{edge [color=crimson] "Consumption" ->"Smart Meter"};
{edge [color=blue2] "Medical" ->tight:Apps};
{edge [color=darkviolet] "Political" ->"Ballot"};
{edge [color=lightskyblue] "Transactional" -> tight:Browser};
{edge [color=lightskyblue] "Transactional" -> tight:Apps;};
{edge [color=lightskyblue] "Transactional" -> {"Reward Cards"; "Paypoint"};};
subgraph cluster_SmartPhone {label = "Smart Phone";
dirType=none;
tight [
shape=none
label=<
<table border="0" cellborder="1" cellspacing="10">
<tr>
<td port="Camera">Camera</td>
</tr>
<tr>
<td port="Apps">Apps</td>
</tr>
<tr>
<td port="Browser">Browser</td>
</tr>
<tr>
<td port="GPS">GPS</td>
</tr>
</table>
>
]
};
v_Automobile [label="Automobile",
penwidth=2,
color=firebrick1];
v_Body [label="Body",
penwidth=2,
color=blue2];
v_Citizen [label="Citizen",
penwidth=2,
color=darkviolet];
v_Consumer [label="Consumer",
penwidth=2,
color=lightskyblue];
v_Home [label="Home",
penwidth=2,
color=crimson];
}
Result:

Related

Graphviz with HTML-like nodes, trying to control arrow shape

I'm trying to create a representation of haproxy redirects and also linking to services behind the haproxy.
I've got the following dot code:
digraph structs {
node [shape=plaintext]
subgraph cluster_haproxy {
label = "haproxy";
haproxy [label=<
<TABLE BORDER="3" CELLBORDER="1" CELLSPACING="50">
<TR>
<TD PORT="80">80</TD>
<TD PORT="443">443</TD>
</TR>
</TABLE>
>];
}
subgraph cluster_service001 {
label = "service001";
service001 [label=<
<TABLE BORDER="3" CELLBORDER="1" CELLSPACING="4">
<TR>
<TD PORT="123456789">123456789</TD>
</TR>
</TABLE>
>];
}
haproxy:80:e -> haproxy:443:w;
haproxy:443 -> service001:123456789;
}
Which results in this image, showing that port 80 gets redirected to port 443:
What I'm looking to achieve is just a straight arrow from 80 to 443, without any curve.
Do note that there may well be more ports on that same line.
I tried adding splines=line
I tried it with record-based nodes, giving them a rank=same to keep them next to each other, but that gives me this error message:
Warning: flat edge between adjacent nodes one of which has a record
shape - replace records with HTML-like labels
I tried it with box shape, but that put the items in the wrong order left to right.
Running out of ideas here. Any suggestions for how to properly do something like this would very much be appreciated.
GraphViz do not handle internal edges in nodes vell
I suggest you switch to 2 levels of clusters instead like here:
digraph structs {
node [shape=plaintext]
subgraph cluster_haproxy {
label = "haproxy";
subgraph cluster_haproxy2 {
penwidth=3
label=""
node[shape=rect]
{rank=same
80
443}
}
}
subgraph cluster_service001 {
label = "service001";
subgraph cluster_service001 {
penwidth=3
label=""
123456789[shape=rect]
}
}
80 -> 443
443 -> 123456789
}

Is there a way to add arrow from a token/word to another in graphviz?

I'm wondering if it's possible to create something like this usint graphviz, where an arrow points from a token/word to the other, instead of a node.
It is possible to simulate (a lot of things) with tables, though it's usually very ugly in source code:
digraph {
node [shape=plain]
node1 [
label=<
<table cellspacing="0" bgcolor="#d0e2f2" cellborder="0">
<tr><td></td></tr>
<tr><td port="label">foo bar</td></tr>
<tr><td></td></tr>
</table>>
]
node2 [
label=<
<table cellspacing="0" bgcolor="#d0e2f2" cellborder="0">
<tr><td></td></tr>
<tr><td port="label">baz qux</td></tr>
<tr><td></td></tr>
</table>>
]
node1:label:n -> node2:label:n [constraint=false]
}
Result:
What I did here:
I used a plain node shape and HTML-like label syntax to create a table:
node [shape=plain]
node1 [
label=<>
]
I added 3 rows for my table, first and last one being empty:
<tr><td></td></tr>
<tr><td port="label">foo bar</td></tr>
<tr><td></td></tr>
The middle row contains the actual label. Also, to be able to point an edge to specific cell I've added a port to it: <td port="label">foo bar</td>.
Finally when defining an edge I've specified the ports to be connected (documentation on ports):
node1:label:n -> node2:label:n

give each wedge a different label in style=wedged in DOT graphviz

I could not figure out looking at the Graphviz docs if there is a way to specify different labels for each wedge in a circle with style=wedged. e.g. below node draws a circle divided into 3 wedges with three colors.
nodepie [shape = "circle" style = "wedged" fillcolor = "green:red:yellow"];
I want to put different numbers on each wedge.
Could anybody help?
Thanks for your help.
Sorry, but I don't think this is possible; I've tried before. Even using weird combinations of html labels I wasn't able to get a satisfactory result. There's nothing in the label documentation that even hints at a method for achieving this.
Sorry I don't have a more positive answer for you, but in the case I think it's "no." As you can see, alignment is a mess; it might be feasible if the pie had four wedges and the html label could be superimposed on top, two rows and two columns. But with three it's hard to imagine a layout that would delight:
digraph x{
nodepie [shape = "circle" style = "wedged" fillcolor = "green:red:yellow"
label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0"> <TR><TD PORT="f0">one</TD><TD>two</TD></TR> </TABLE>>];
}
Workaround for three wedgets (of approximately same size);
digraph x{
nodepie [shape = "circle" style = "wedged" fillcolor = "green:red:yellow"
label=<
<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0">
<TR>
<TD PORT="f0" ROWSPAN="2">one</TD>
<TD ROWSPAN="2"> </TD>
<TD>two</TD>
</TR>
<TR>
<TD PORT="f0">tree</TD>
</TR>
</TABLE>
>];
}
If you create a html-table with elements using the same colors as the WEDGE, you can have your labels next to the pie.
digraph structs {
p01 [shape = none
label = <<table border="0" cellspacing="0">
<tr><td bgcolor="blue">P01</td></tr>
<tr><td bgcolor="red">P02</td></tr>
<tr><td bgcolor="yellow">P03</td></tr>
<tr><td bgcolor="green">P04</td></tr>
</table>>]
node [shape=circle]
pie [label="wedged"
style=wedged
color=none
fillcolor="blue;0.4:red;0.1:yellow;0.2:green"
fontcolor=pink]
}
Result:

Graphviz, distance between table node and edge

How can I remove the space between the border of the table and the edge ?
The node seems to have a margin outside of the table.
The definition of the graph in dot:
strict digraph {
rankdir=RL;
node [shape=none, style=filled, fillcolor=aliceblue, fontname=verdana];
Model [shape=none, label=<<table border="0" cellspacing="0">
<tr><td border="1" bgcolor="lightblue">Model</td></tr>
<tr><td border="1" align="left">Name</td></tr>
<tr><td border="1" align="left">Description</td></tr>
<tr><td border="1" align="left">Universe</td></tr>
</table>>];
Universe [shape=none, label=<<table border="0" cellspacing="0">
<tr><td border="1" bgcolor="lightblue">Universe</td></tr>
<tr><td border="1" align="left">Name</td></tr>
<tr><td border="1" align="left">Description</td></tr>
</table>>];
Model -> Universe
}
The resulting png is:
Using graphviz version 2.38.0.
Any comment much appreciated.
Employ the margin keyword:
node [shape=none, style=filled, fillcolor=aliceblue, fontname=verdana, margin=0];
That'll reduce that aliceblue area around the tables to nothing and make the arrows really connect the tables.

Making a Legend/Key in GraphViz

I’d like to include a legend or key in my GraphViz diagram. I’m having trouble figuring out what code to use, though. I also want to put it in a corner, but the only coord I know for sure is the bottom-left: pos="10,10!".
Does anyone know how I can get this to work?
digraph {
rankdir=LR
node [shape=plaintext]
subgraph cluster_01 {
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td align="right" port="i1">item 1</td></tr>
<tr><td align="right" port="i2">item 2</td></tr>
<tr><td align="right" port="i3">item 3</td></tr>
<tr><td align="right" port="i4">item 4</td></tr>
</table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td port="i1"> </td></tr>
<tr><td port="i2"> </td></tr>
<tr><td port="i3"> </td></tr>
<tr><td port="i4"> </td></tr>
</table>>]
key:i1:e -> key2:i1:w [style=dashed]
key:i2:e -> key2:i2:w [color=gray]
key:i3:e -> key2:i3:w [color=peachpuff3]
key:i4:e -> key2:i4:w [color=turquoise4, style=dotted]
}
...
I used dot.
I'm deeply convinced that graphviz should not be used this way, but you may use HTML labels to achieve what you want:
digraph {
Foo -> Bar -> Test;
Foo -> Baz -> Test;
{ rank = sink;
Legend [shape=none, margin=0, label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR>
<TD COLSPAN="2"><B>Legend</B></TD>
</TR>
<TR>
<TD>Foo</TD>
<TD><FONT COLOR="red">Foo</FONT></TD>
</TR>
<TR>
<TD>Bar</TD>
<TD BGCOLOR="RED"></TD>
</TR>
<TR>
<TD>Baz</TD>
<TD BGCOLOR="BLUE"></TD>
</TR>
<TR>
<TD>Test</TD>
<TD><IMG src="so.png" SCALE="False" /></TD>
</TR>
<TR>
<TD>Test</TD>
<TD CELLPADDING="4">
<TABLE BORDER="1" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0">
<TR>
<TD BGCOLOR="Yellow"></TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
>];
}
}
That's what this looks like:
The positioning of the Legend has to be done like any other node (I used rank=sink to get it to the bottom) - you may play with its margin attribute for fine-tuning the position.
Edit:
Without using labels, that may be the direction to go for - I'm not sure whether it is to completely eliminate ranksep.
digraph {
mindist=0;
ranksep=0;
nodesep=0;
node[shape=box,margin="0,0",width=1, height=0.5];
edge [style=invis];
Legend[width=2];
Legend -> Foo;
Legend -> FooValue;
Foo -> Bar;
FooValue -> BarValue
Bar -> Baz;
BarValue -> BazValue;
edge [constraint=false];
Foo -> FooValue;
Bar -> BarValue
Baz -> BazValue;
}
Resulting in:
Lots of these anwers show nice ways to render a legend, and that's useful, but a major unaddressed problem is integration of that legend in the same output as the main graph. This causes all sorts of problems because things like rankdir and rank positions "leak" between the main diagram and the legend, making it hard to improve one without breaking the other.
After trying several ways of embedding a key within the main GraphViz image, I've decided that for me, it makes more sense to simply put the legend into its own, separate dot file, render it as it's own, separate, image, and then display the images side-by-side in my documents/pages.
This has a few advantages:
The .dot source code is substantially simpler.
It's very easy to change the rankdir of the legend graph to display the nodes above one another, or side-by-side, to produce a key that either sits to the right of the main image, or below it.
No leaking of things like rank positions from the main graph into the legend.
For example:
I had some luck with the following. I didn't like how wide it was, but otherwise it worked.
subgraph cluster1 {
label = "Legend" ;
shape = rectangle ;
color = black ;
a [style=invis] ;
b [style=invis] ;
c [style=invis] ;
d [style=invis] ;
c -> d [label="only ts", style=dashed, fontsize=20] ;
a -> b [label="ts and js", fontsize=20] ;
gui -> controller [style=invis] ;
view -> model [style=invis] ;
builtins -> utilities [style=invis] ;
gui [style=filled, fillcolor="#ffcccc"] ;
controller [style=filled, fillcolor="#ccccff"] ;
view [style=filled, fillcolor="#ccffcc"] ;
model [style=filled, fillcolor="#ffccff"] ;
builtins [style=filled, fillcolor="#ffffcc"] ;
utilities ;
"external libraries" [shape=rectangle] ;
}
The result was
subgraph cluster_01 {
label = "Legend";
node [shape=point]
{
rank=same
d0 [style = invis];
d1 [style = invis];
p0 [style = invis];
p1 [style = invis];
s0 [style = invis];
s1 [style = invis];
}
d0 -> d1 [label=deprecated style=dashed]
p0 -> p1 [label=proposed style=dotted]
s0 -> s1 [label=stable]
}
There are some problems if you use graph [splines=ortho] : the lines are in inverse order.
Dot source:
digraph {
rankdir=LR
node [shape=plaintext]
graph [splines=ortho]
subgraph cluster_01 {
label = "Legend";
key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td align="right" port="i1">item 1</td></tr>
<tr><td align="right" port="i2">item 2</td></tr>
<tr><td align="right" port="i3">item 3</td></tr>
<tr><td align="right" port="i4">item 4</td></tr>
<tr><td align="right" port="i5">item 5</td></tr>
</table>>]
key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0">
<tr><td port="i1" bgcolor='greenyellow'> </td></tr>
<tr><td port="i2"> </td></tr>
<tr><td port="i3"> </td></tr>
<tr><td port="i4"> </td></tr>
<tr><td port="i5"> </td></tr>
</table>>]
key:i1:e -> key2:i1:w [color=red]
key:i2:e -> key2:i2:w [color=gray]
key:i3:e -> key2:i3:w [color=peachpuff3]
key:i4:e -> key2:i4:w [color=turquoise4, style=dotted]
key:i5:e -> key2:i5:w [color=red, style=dotted]
}
}
I'm trying to do the same. I have been using a subgraph to make a key of node types:
digraph G {
rankdir=RL;
graph [fontsize=10 fontname="Verdana"];
node [style=filled height=0.55 fontname="Verdana" fontsize=10];
subgraph cluster_key {
label="Key";
progress [fillcolor="wheat" label="In progress"];
todo [label="To do"];
done [fillcolor=palegreen3 label="Done"];
not_our [fillcolor=none label="Not our\nteam"];
numbers [color=none label="Numbers\nrepresent\nperson\ndays"];
progress -> done [style=invis];
todo -> progress [style=invis];
not_our -> todo [style=invis];
numbers -> not_our [style=invis];
}
mappings [fillcolor=palegreen3];
identifiers [fillcolor=palegreen3];
hyperwarp [fillcolor=wheat];
ghost [fillcolor=none]
UI [fillcolor=none]
events [fillcolor=wheat];
flag [fillcolor=palegreen3];
groups [fillcolor=wheat];
types [fillcolor=wheat];
instances [];
resources [];
optimize [];
remove_flag [];
persist [];
approval [];
edge [style="" dir=forward fontname="Verdana" fontsize=10];
types -> flag;
groups -> events;
events -> {flag mappings identifiers};
ghost -> hyperwarp;
UI -> ghost;
resources -> identifiers;
optimize -> groups;
hyperwarp -> flag;
instances -> {ghost UI types events hyperwarp flag};
resources -> {groups flag};
remove_flag -> approval;
persist -> approval;
approval -> {types resources instances};
}
which results in
But on reflection, seeing the difficulty I'm having to position the legend alongside the main graph, the way the position of node rankings in the main graph affects those in the legend, and the complication in the source that this introduces, I'm tempted to try a different approach (see my other answer, use a separate graph for the key)
This works well for simpler legends (from: https://forum.graphviz.org/t/adding-key-or-legend/351)
digraph l {
subgraph clusterMain {
graph [labelloc="b" labeljust="r" label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD>left 1</TD><TD>right 1</TD></TR>
<TR><TD>left 2</TD><TD>right 2</TD></TR>
</TABLE>>];
"x" "y"
a -> b -> c
}
}

Resources