I have a collection of digraphs encoded in DOT language, and i want to merge them into a single digraph where nodes with the same name in different input graphs are merged together.
For example given the following files:
1.dot
:
digraph {
A -> B
A -> C
}
2.dot
:
digraph {
D -> E
E -> F
}
3.dot
:
digraph {
D -> G
G -> A
}
I would like to obtain the following result.dot
:
digraph {
subgraph {
A -> B
A -> C
}
subgraph {
D -> E
E -> F
}
subgraph {
D -> G
G -> A
}
}
I tried to use gvpack
but it renames duplicate nodes.
> gvpack -u 1.dot 2.dot 3.dot
Warning: node D in graph[2] %15 already defined
Some nodes will be renamed.
digraph root {
node [label="\N"];
{
node [label="\N"];
A -> B;
A -> C;
}
{
node [label="\N"];
D -> E;
E -> F;
}
{
node [label="\N"];
D_gv1 -> G;
G -> A_gv1;
}
}
I found a similar question on SO that suggest using sed
to rename the renamed nodes, but that doesn't seem very clean.
Is there a way to merge the graphs the way i would like them?
I ended up using a Java library to perform the merge, and much more!
With the library i could easily tap into the data structure, change nodes if need be, and add attributes to the graph.
A quick example in Kotlin:
// prepare root graph and set direction
val wamap = mutGraph("wamap")
.setDirected(true)
wamap.graphAttrs().add(RankDir.LEFT_TO_RIGHT)
// add subgraphs from the content of .gv files from disk
Files.walk(Paths.get("D:\\src\\work\\Wamap"), 1)
.filter { Files.isRegularFile(it) }
.filter { it.fileName.toString().endsWith(".gv") }
.map { Parser.read(it.toFile()) }
.forEach { it.addTo(wamap) }
// normalize node names to lowercase, to ensure nodes with same name are the same node
wamap.graphs()
.flatMap { it.nodes() }
.forEach { it.setName(it.name().toString().toLowerCase()) }
// output as file, but also render the image directly with all the possible Graphviz layout engines
File("out/wamap.gv").writeText(wamap.toString())
Engine.values()
.forEach { engine ->
Graphviz.fromGraph(wamap).engine(engine).render(Format.PNG).toFile(File("out/wamap-$engine.png"))
}