GraphMakie

This is the Documentation for GraphMakie.

This Package consists of two parts: a plot recipe for graphs types from Graphs.jl and some helper functions to add interactions to those plots.

There are also plot examples and interaction examples pages.

The graphplot Recipe

GraphMakie.graphplot โ€” Function
graphplot(graph::AbstractGraph)
graphplot!(ax, graph::AbstractGraph)

Creates a plot of the network graph. Consists of multiple steps:

  • Layout the nodes: see layout attribute. The node position is accessible from outside the plot object p as an observable using p[:node_pos].
  • plot edges as edgeplot-plot
  • if arrow_show plot arrowheads as scatter-plot
  • plot nodes as scatter-plot
  • if nlabels!=nothing plot node labels as text-plot
  • if elabels!=nothing plot edge labels as text-plot

The main attributes for the subplots are exposed as attributes for graphplot. Additional attributes for the scatter, edgeplot and text plots can be provided as a named tuples to node_attr, edge_attr, nlabels_attr and elabels_attr.

Most of the arguments can be either given as a vector of length of the edges/nodes or as a single value. One might run into errors when changing the underlying graph and therefore changing the number of Edges/Nodes.

Attributes

Main attributes

  • layout=Spring(): function AbstractGraph->Vector{Point} or Vector{Point} determines the base layout
  • node_color=automatic: Defaults to scatter_theme.color in absence of ilabels.
  • node_size=automatic: Defaults to scatter_theme.markersize in absence of ilabels. Otherwise choses node size based on ilabels size.
  • node_marker=automatic: Defaults to scatter_theme.marker in absence of ilabels.
  • node_strokewidth=automatic Defaults to scatter_theme.strokewidth in absence of ilabels.
  • node_attr=(;): List of kw arguments which gets passed to the scatter command
  • edge_color=lineseg_theme.color: Color for edges.
  • edge_width=lineseg_theme.linewidth: Pass a vector with 2 width per edge to get pointy edges.
  • edge_attr=(;): List of kw arguments which gets passed to the linesegments command
  • arrow_show=Makie.automatic: Bool, indicate edge directions with arrowheads? Defaults to Graphs.is_directed(graph).
  • arrow_marker='โžค'
  • arrow_size=scatter_theme.markersize: Size of arrowheads.
  • arrow_shift=0.5: Shift arrow position from source (0) to dest (1) node. If arrow_shift=:end, the arrowhead will be placed on the surface of the destination node (assuming the destination node is circular).
  • arrow_attr=(;): List of kw arguments which gets passed to the scatter command

Node labels

The position of each label is determined by the node position plus an offset in data space.

  • nlabels=nothing: Vector{String} with label for each node
  • nlabels_align=(:left, :bottom): Anchor of text field.
  • nlabels_distance=0.0: Pixel distance from node in direction of align.
  • nlabels_color=labels_theme.color
  • nlabels_offset=nothing: Point or Vector{Point} (in data space)
  • nlabels_fontsize=labels_theme.fontsize
  • nlabels_attr=(;): List of kw arguments which gets passed to the text command

Inner node labels

Put labels inside the marker. If labels are provided, change default attributes to node_marker=Circle, node_strokewidth=1 and node_color=:gray80. The node_size will match size of the ilabels.

  • ilabels=nothing: Vector with label for each node
  • ilabels_color=labels_theme.color
  • ilabels_fontsize=labels_theme.fontsize
  • ilabels_attr=(;): List of kw arguments which gets passed to the text command

Edge labels

The base position of each label is determined by src + shift*(dst-src). The additional distance parameter is given in pixels and shifts the text away from the edge.

  • elabels=nothing: Vector{String} with label for each edge
  • elabels_align=(:center, :center): Anchor of text field.
  • elabels_side = :left: Side of the edge to put the edge label text
  • elabels_distance=Makie.automatic: Pixel distance of anchor to edge. The direction is decided based on elabels_side
  • elabels_shift=0.5: Position between src and dst of edge.
  • elabels_rotation=Makie.automatic: Angle of text per label. If nothing this will be determined by the edge angle. If automatic it will also point upwards making it easy to read.
  • elabels_offset=nothing: Additional offset in data space
  • elabels_color=labels_theme.color
  • elabels_fontsize=labels_theme.fontsize
  • elabels_attr=(;): List of kw arguments which gets passed to the text command

Curvy edges & self edges/loops

  • edge_plottype=Makie.automatic(): Either automatic, :linesegments or :beziersegments. :beziersegments are much slower for big graphs!

Self edges / loops:

  • selfedge_size=Makie.automatic(): Size of selfloop (dict/vector possible).
  • selfedge_direction=Makie.automatic(): Direction of center of the selfloop as Point2 (dict/vector possible).
  • selfedge_width=Makie.automatic(): Opening of selfloop in rad (dict/vector possible).
  • Note: If valid waypoints are provided for selfloops, the selfedge attributes above will be ignored.

High level interface for curvy edges:

  • curve_distance=0.1:

    Specify a distance of the (now curved) line to the straight line in data space. Can be single value, array or dict. User provided tangents or waypoints will overrule this property.

  • curve_distance_usage=Makie.automatic():

    If Makie.automatic(), only plot double edges in a curvy way. Other options are true and false.

Tangents interface for curvy edges:

  • tangents=nothing:

    Specify a pair of tangent vectors per edge (for src and dst). If nothing (or edge idx not in dict) draw a straight line.

  • tfactor=0.6:

    Factor is used to calculate the bezier waypoints from the (normalized) tangents. Higher factor means bigger radius. Can be tuple per edge to specify different factor for src and dst.

  • Note: Tangents are ignored on selfloops if no waypoints are provided.

Waypoints along edges:

  • waypoints=nothing

    Specify waypoints for edges. This parameter should be given as a vector or dict. Waypoints will be crossed using natural cubic splines. The waypoints may or may not include the src/dst positions.

  • waypoint_radius=nothing

    If the attribute waypoint_radius is nothing or :spline the waypoints will be crossed using natural cubic spline interpolation. If number (dict/vector possible), the waypoints won't be reached, instead they will be connected with straight lines which bend in the given radius around the waypoints.

source

Passing arguments

The recipe can handle a range of argument types. For all the arguments that support a collection of configurations per element, you pass-in a Vector. However you can also pass in a Dict or a DefaultDict to only specify a configuration for a specific element of interest, while the rest get the default value. The keys of the Dictionaries are the Int index of the element or the Edge when reasonable. See some demonstration on Changes of node and label sizes, Dict and DefaultDict and Use Dict{Edge} for edge arguments.

Network Layouts

The layout algorithms are provided by NetworkLayout.jl. See the docs for a list of available layouts.

A layout has to be a function f(g::AbstractGraph) -> pos::Vector{Point}. You can also provide your own layouts or use other packages like LayeredLayouts.jl for DAG (see also the Dependency Graph of a Package example).

using LayeredLayouts
function mylayout(g::SimpleGraph)
   xs, ys, _ = solve_positions(Zarate(), g)
   return Point.(zip(xs, ys))
end

Predefined Interactions

GraphMakie.jl provides some pre-built interactions to enable drag&drop of nodes and edges as well as highlight on hover.

To try them all use the following code in a GLMakie environment.

using GLMakie
using GraphMakie
using Graphs
g = wheel_graph(10)
f, ax, p = graphplot(g, edge_width=[3 for i in 1:ne(g)],
                     node_size=[10 for i in 1:nv(g)])

deregister_interaction!(ax, :rectanglezoom)
register_interaction!(ax, :nhover, NodeHoverHighlight(p))
register_interaction!(ax, :ehover, EdgeHoverHighlight(p))
register_interaction!(ax, :ndrag, NodeDrag(p))
register_interaction!(ax, :edrag, EdgeDrag(p))
GraphMakie.NodeHoverHighlight โ€” Function
NodeHoverHeighlight(p::GraphPlot, factor=2)

Magnifies the node_size of node under cursor by factor.

Example

julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, node_size = [20 for i in 1:nv(g)])
julia> register_interaction!(ax, :nodehover, NodeHoverHighlight(p))
source
GraphMakie.EdgeHoverHighlight โ€” Function
EdgeHoverHeighlight(p::GraphPlot, factor=2)

Magnifies the edge_width of edge under cursor by factor. If arrow_size isa Vector{<:Real} it also magnifies the arrow scatter.

Example

julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width = [3 for i in 1:ne(g)],
                               arrow_size=[10 for i in 1:ne(g)])
julia> register_interaction!(ax, :nodehover, EdgeHoverHighlight(p))
source
GraphMakie.NodeDrag โ€” Function
NodeDrag(p::GraphPlot)

Allows drag and drop of Nodes. Please deregister the :rectanglezoom interaction.

Example

julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, node_size = [10 for i in 1:nv(g)])
julia> deregister_interaction!(ax, :rectanglezoom)
julia> register_interaction!(ax, :nodehover, NodeHoverHighlight(p))
julia> register_interaction!(ax, :nodedrag, NodeDrag(p))
source
GraphMakie.EdgeDrag โ€” Function
EdgeDrag(p::GraphPlot)

Allows drag and drop of Edges. Please deregister the :rectanglezoom interaction.

Example

julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, edge_width = [3 for i in 1:ne(g)])
julia> deregister_interaction!(ax, :rectanglezoom)
julia> register_interaction!(ax, :edgehover, EdgeHoverHighlight(p))
julia> register_interaction!(ax, :edgedrag, EdgeDrag(p))
source

Interaction Interface

GraphMakie.jl provides some helper functions to register interactions to your graph plot. There are special interaction types for hovering, clicking and dragging nodes and edges. For more information on the axis interaction please consult the Makie.jl docs.

The general idea is to create some handler type, provide some action function and register it as an interaction with the axes.

Click Interactions

GraphMakie.NodeClickHandler โ€” Function
NodeClickHandler(fun)

Initializes ClickHandler for nodes. Calls function

fun(idx, event, axis)

on left-click events where idx is the node index.

Example

julia> using Makie.Colors
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size=30, node_color=[colorant"red" for i in 1:nv(g)])
julia> function action(idx, event, axis)
           p.node_color[][idx] = rand(RGB)
           p.node_color[] = p.node_color[]
       end
julia> register_interaction!(ax, :nodeclick, NodeClickHandler(action))
source
GraphMakie.EdgeClickHandler โ€” Function
EdgeClickHandler(fun)

Initializes ClickHandler for edges. Calls function

fun(idx, event, axis)

on left-click events where idx is the edge index.

Example

julia> using Makie.Colors
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width=4, edge_color=[colorant"black" for i in 1:ne(g)])
julia> function action(idx, event, axis)
           p.edge_color[][idx] = rand(RGB)
           p.edge_color[] = p.edge_color[]
       end
julia> register_interaction!(ax, :edgeclick, EdgeClickHandler(action))
source

Hover Interactions

GraphMakie.NodeHoverHandler โ€” Function
NodeHoverHandler(fun)

Initializes HoverHandler for nodes. Calls function

fun(hoverstate, idx, event, axis)

with hoverstate=true on hover and false at the end of hover. idx is the node index.

Example

julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size = [20 for i in 1:nv(g)])
julia> function action(state, idx, event, axis)
           p.node_size[][idx] = state ? 40 : 20
           p.node_size[] = p.node_size[] #trigger observable
       end
julia> register_interaction!(ax, :nodehover, NodeHoverHandler(action))
source
GraphMakie.EdgeHoverHandler โ€” Function
EdgeHoverHandler(fun)

Initializes HoverHandler for edges. Calls function

fun(hoverstate, idx, event, axis)

with hoverstate=true on hover and false at the end of hover. idx is the edge index.

Example

julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width = [3.0 for i in 1:ne(g)])
julia> function action(state, idx, event, axis)
           p.edge_width[][idx] = state ? 6.0 : 3.0
           p.edge_width[] = p.edge_width[] #trigger observable
       end
julia> register_interaction!(ax, :edgehover, EdgeHoverHandler(action))
source

Drag Interactions

GraphMakie.NodeDragHandler โ€” Function
NodeDragHandler(fun)

Initializes DragHandler for Nodes. Calls function

fun(dragstate, idx, event, axis)

where dragstate=true during the drag and false at the end of the drag, the last time fun is triggered. idx is the node index.

Example

julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size=20)
julia> deregister_interaction!(ax, :rectanglezoom)
julia> function action(state, idx, event, axis)
           p[:node_pos][][idx] = event.data
           p[:node_pos][] = p[:node_pos][]
       end
julia> register_interaction!(ax, :nodedrag, NodeDragHandler(action))
source
GraphMakie.EdgeDragHandler โ€” Function
EdgeDragHandler(fun)

Initializes DragHandler for Edges. Calls function

fun(dragstate, idx, event, axis)

where dragstate=true during the drag and false at the end of the drag, the last time fun is triggered. idx is the edge index.

See EdgeDrag for a concrete implementation. ```

source