2015-09-14

Работа с графами и их визуализация (.NET, C#)

Если в вашем проекте возникла потребность в работе с графами, то вам поможет связка QuickGraph + GraphViz.

Алгоритм внедрения в .NET приложение следующий:
1. Используя пространства имен QuickGraph и QuickGraph.GraphViz, формируем граф и записываем его в файл .dot. Также можно отсортировать граф топологически, это полезно для выявления последовательности выполнения связанных процессов, например, у нас в пакете 20 сборок, и нам нужно откомпилировать все, учитывая связи между сборками. Отсортировав топологически, мы можем найти последовательность компиляции всех сборок...





2. С помощью командной утилиты из dot.exe из пакета GraphViz формируем png-файл и отображаем в своем приложении.
Все просто :) В результате получаем изображение графа с автоматической расстановкой связей и элементов.
Добавлю, что с помощью dot.exe можно экспортировать в другие форматы, например, SVG, GIF, JPEG. У себя мы использовали следующую связку PNG + CMAP карта изображения для тега img. Таким образом, мы получали автоматически сформированное интерактивное изображение процесса с подсказками и ссылками в нашем ASP.NET приложении.

Ниже пример построения графа последовательности одевания человека (C#):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QuickGraph;
using QuickGraph.Algorithms;
using QuickGraph.Graphviz;
using System.Drawing;
using System.Diagnostics;
namespace gr
{
class Program
{
static void Main(string[] args)
{
var g = new AdjacencyGraph<stringTaggedEdge<stringstring>>();
g.AddVertex("Часы");
g.AddVertex("Носки");
g.AddVertex("Туфли");
g.AddVertex("Трусы");
g.AddVertex("Брюки");
g.AddVertex("Рубашка");
g.AddVertex("Ремень");
g.AddVertex("Галстук");
g.AddVertex("Пиджак");
g.AddEdge(new TaggedEdge<stringstring>("Носки""Туфли""a"));
g.AddEdge(new TaggedEdge<stringstring>("Трусы""Туфли""s"));
g.AddEdge(new TaggedEdge<stringstring>("Трусы""Брюки""d"));
g.AddEdge(new TaggedEdge<stringstring>("Брюки""Туфли""f"));
g.AddEdge(new TaggedEdge<stringstring>("Брюки""Ремень""g"));
g.AddEdge(new TaggedEdge<stringstring>("Рубашка""Ремень""h"));
g.AddEdge(new TaggedEdge<stringstring>("Рубашка""Галстук""j"));
g.AddEdge(new TaggedEdge<stringstring>("Галстук""Пиджак""k"));
 g.AddEdge(new TaggedEdge<stringstring>("Ремень""Пиджак""l"));
foreach (var elem in g.TopologicalSort<stringTaggedEdge<stringstring>>())
{
Console.WriteLine(elem);
}
var graphViz = new GraphvizAlgorithm<stringTaggedEdge<stringstring>>(g,@".\", QuickGraph.Graphviz.Dot.GraphvizImageType.Png);
graphViz.FormatVertex += FormatVertex;
graphViz.FormatEdge += FormatEdge;
graphViz.Generate(new FileDotEngine(), "ww");
Console.ReadLine();
}
private static void FormatVertex(object sender, FormatVertexEventArgs<string> e)
{
e.VertexFormatter.Label = e.Vertex;
e.VertexFormatter.Shape = QuickGraph.Graphviz.Dot.GraphvizVertexShape.Box;
e.VertexFormatter.StrokeColor = Color.Yellow;
e.VertexFormatter.Font = new Font(FontFamily.GenericSansSerif, 12);
}
private static void FormatEdge(object sender, FormatEdgeEventArgs<string,TaggedEdge<stringstring>> e)
{
e.EdgeFormatter.Head.Label = e.Edge.Target;
e.EdgeFormatter.Tail.Label = e.Edge.Source;
e.EdgeFormatter.Font = new Font(FontFamily.GenericSansSerif, 8);
e.EdgeFormatter.FontColor = Color.Red;
e.EdgeFormatter.StrokeColor = Color.Gray;
 }
}
}
После выполнения приложения получаем файл ww.dot:
digraph G { 0 [color="#FFFF00FF", fontsize=12, label="Часы", shape=box, fontname="Microsoft Sans Serif"]; 1 [color="#FFFF00FF", fontsize=12, label="Носки", shape=box, fontname="Microsoft Sans Serif"]; 2 [color="#FFFF00FF", fontsize=12, label="Туфли", shape=box, fontname="Microsoft Sans Serif"]; 3 [color="#FFFF00FF", fontsize=12, label="Трусы", shape=box, fontname="Microsoft Sans Serif"]; 4 [color="#FFFF00FF", fontsize=12, label="Брюки", shape=box, fontname="Microsoft Sans Serif"]; 5 [color="#FFFF00FF", fontsize=12, label="Рубашка", shape=box, fontname="Microsoft Sans Serif"]; 6 [color="#FFFF00FF", fontsize=12, label="Ремень", shape=box, fontname="Microsoft Sans Serif"]; 7 [color="#FFFF00FF", fontsize=12, label="Галстук", shape=box, fontname="Microsoft Sans Serif"]; 8 [color="#FFFF00FF", fontsize=12, label="Пиджак", shape=box, fontname="Microsoft Sans Serif"]; 1 -> 2 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Носки", fontname="Microsoft Sans Serif", headlabel="Туфли"]; 3 -> 2 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Трусы", fontname="Microsoft Sans Serif", headlabel="Туфли"]; 3 -> 4 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Трусы", fontname="Microsoft Sans Serif", headlabel="Брюки"]; 4 -> 2 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Брюки", fontname="Microsoft Sans Serif", headlabel="Туфли"]; 4 -> 6 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Брюки", fontname="Microsoft Sans Serif", headlabel="Ремень"]; 5 -> 6 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Рубашка", fontname="Microsoft Sans Serif", headlabel="Ремень"]; 5 -> 7 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Рубашка", fontname="Microsoft Sans Serif", headlabel="Галстук"]; 6 -> 8 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Ремень", fontname="Microsoft Sans Serif", headlabel="Пиджак"]; 7 -> 8 [ fontsize=8, fontcolor="#FF0000FF", color="#808080FF", taillabel="Галстук", fontname="Microsoft Sans Serif", headlabel="Пиджак"]; }

Формируем изображение с помощью dot.exe:
dot -T png ww.dot > ww.png

Итоговое изображение:




Оригинальная статья расположена по адресу....--->>