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.

about Graphs.jl and LightGraphs.jl

Starting from v0.3 GraphMakie.jl switches from LightGraphs.jl to Graphs.jl as the for the underlying graph package. See this discourse post for more information. If you want to use LightGraphs.jl please specifically ] add GraphMakie@0.2!

There are also plot examples and interaction examples pages.

The graphplot Recipe

GraphMakie.graphplotFunction
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} determines the base layout
  • node_color=scatter_theme.color
  • node_size=scatter_theme.markersize
  • node_marker=scatter_theme.marker
  • 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_size=scatter_theme.markersize: Size of arrowheads.
  • arrow_shift=0.5: Shift arrow position from source (0) to dest (1) node.
  • 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_textsize=labels_theme.textsize
  • nlabels_attr=(;): List of kw arguments which gets passed to the text command

Edge labels

The base position of each label is determinded 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, :bottom): Anchor of text field.
  • elabels_distance=0.0: Pixel distance of anchor to edge.
  • elabels_shift=0.5: Position between src and dst of edge.
  • elabels_opposite=Int[]: List of edge indices, for which the label should be displayed on the opposite side
  • elabels_rotation=nothing: Angle of text per label. If nothing this will be determined by the edge angle!
  • elabels_offset=nothing: Additional offset in data space
  • elabels_color=labels_theme.color
  • elabels_textsize=labels_theme.textsize
  • 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 self-edge-loop (dict/vector possible).
  • selfedge_direction=Makie.automatic(): Direction of self-edge-loop as Point2 (dict/vector possible).
  • selfedge_width=Makie.automatic(): Opening of selfloop in rad (dict/vector possible).

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 proivded 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.

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 number (dict/vector possible) bent lines within radius of waypoints.

source

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.NodeHoverHighlightFunction
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.EdgeHoverHighlightFunction
EdgeHoverHeighlight(p::GraphPlot, factor=2)

Magnifies the edge_width of edge under cursor by factor. If arrow_size isa Vector{<:Real} it also magnefies 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.NodeDragFunction
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.EdgeDragFunction
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 draging 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.NodeClickHandlerFunction
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.EdgeClickHandlerFunction
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.NodeHoverHandlerFunction
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.EdgeHoverHandlerFunction
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.NodeDragHandlerFunction
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.EdgeDragHandlerFunction
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