Visualizing Graph Structures Using Go and Graphviz

Graph data structures are perfectly suited for modeling relations and networks. Visualizing graphs in Go takes two things: A graph library supporting DOT, and Graphviz.

August 1, 2022 in Go

A popular way to draw graphs is to describe the graph in the DOT language and pass this description to a rendering tool such as Graphviz. graph is a generic Go graph library for creating graph structures, performing operations on them, and generating a DOT description of them.

Preparation

If you want to try this demo yourself, you can do so by setting up an example project:

  1. Install Go 1.18 and Graphviz.
  2. Initialize a Go module in a new directory, for example go mod init mygraph.
  3. Get the library using go get github.com/dominikbraun/graph.
  4. Create a file called main.go.

Creating a Graph

In this example, we're going to create a simple graph g: It consists of four nodes (or vertices) connected by three directed edges. The vertices are integer values, so this is a graph of integers.

package main

import "github.com/dominikbraun/graph"

func main() {
    g := graph.New(graph.IntHash, graph.Directed())

    _ = g.AddVertex(1)
    _ = g.AddVertex(2)
    _ = g.AddVertex(3)
    _ = g.AddVertex(4)

    _ = g.AddEdge(1, 2)
    _ = g.AddEdge(1, 3)
    _ = g.AddEdge(3, 4)
}

Each vertex is identified by a unique hash. To obtain the hash value for a vertex, the graph needs a hashing function which is passed to New. In our case, this is the predefined IntHash function that accepts an integer and returns that integer as a hash value at the same time.

Custom data types as vertices and custom hashing functions are supported as well, see Hashes.

A variety of functional options may be called when creating the graph, for example Directed to make the graph directed or Acyclic to permit cycles. Vertex adds a vertex with the given value, and Edge creates an edge between two vertices.

Generating a DOT File

The next step is to generate a DOT description for the graph, which can be accomplished using the included draw package. Any io.Writer can be used as an output target. In this demo, we're going to write the DOT description into a file called my-graph.gv:

file, _ := os.Create("my-graph.gv")
_ = draw.DOT(g, file)

With these additions, the Go program should ultimately look as follows:

package main

import (
    "os"

    "github.com/dominikbraun/graph"
    "github.com/dominikbraun/graph/draw"
)

func main() {
    g := graph.New(graph.IntHash, graph.Directed())

    _ = g.AddVertex(1)
    _ = g.AddVertex(2)
    _ = g.AddVertex(3)
    _ = g.AddVertex(4)

    _ = g.AddEdge(1, 2)
    _ = g.AddEdge(1, 3)
    _ = g.AddEdge(3, 4)

    file, _ := os.Create("my-graph.gv")
    _ = draw.DOT(g, file)
}

Running go run main.go will create the graph and yield the corresponding DOT file.

Rendering the Graph

This DOT file can be interpreted by a variety of renderers, the most popular one being Graphviz. After installing Graphviz, you can render the graph as an SVG using the following command:

dot -Tsvg -O my-graph.gv

directed graph

Adding Edge Attributes

The DOT language supports a number of attributes, for example for coloring edges. Using graph, arbitrary edge attributes can be added and included in the DOT description when visualizing a graph. An edge like this will be colored red:

_ = g.AddEdge(1, 2, graph.EdgeAttribute("color", "red"))

directed graph with a red edge

You can find further examples and documentation on GitHub.

💫graph: A library for creating generic graph data structures and modifying, analyzing, and visualizing them.
💫
graph: A library for creating generic graph data structures and modifying, analyzing, and visualizing them.