如何从graphviz的布局中获取坐标?

Posted

技术标签:

【中文标题】如何从graphviz的布局中获取坐标?【英文标题】:How to get the coordinates from layout from graphviz? 【发布时间】:2012-12-06 00:02:15 【问题描述】:

我一直在使用 pygraph 进行一些项目。我完成了this example,它工作正常。

现在,问题如下:图形是以图片格式 (gif) 绘制的。我需要的是为 gif 图像上显示的图形布局获取每个节点的实际坐标。我该怎么做呢?我一直在尝试和尝试,但找不到解决这个问题的方法。我认为问题的解决方案会以某种方式通过操纵以下两行之一:

gv.layout(gvv,'dot')
gv.render(gvv,'png','europe.png')

提前致谢!

【问题讨论】:

您能详细说明您的尝试吗?目前尚不清楚您的问题是什么。您没有设法从节点属性中获取位置吗? 我认为我的问题很清楚 - 我想提取 gv.render 在文件“europe.png”中绘制的节点的坐标。除了“print gv.layout”,我没有尝试任何东西,但它返回“True”。我找到了 gv.layout 的来源,但它只返回“_gv.layout”,我找不到这个来源。所以,我真的不知道每个节点的坐标分配发生在哪里。我尝试了“打印 gr.nodes”,但在那里找不到坐标。任何想法如何从 gv 对象中提取坐标? 有关于这个主题的任何消息吗?我已经专门针对 networkx-pygraphviz-graphviz 堆栈提出了类似的问题,但尚未得到答案。贝尔菲戈尔,你现在有办法解决吗? 【参考方案1】:

您可以使用以下命令将布局信息添加到图表中:

gv.render(gvv)

然后找出一个节点的位置得到它的属性pos

n_france = gv.findnode(gvv, "France")
pos = gv.getv(n_france, "pos")

然后根据您想要做什么,您可能需要将点坐标转换为 png 图像坐标。您可以从这里获得有用的信息:

http://www.graphviz.org/faq/#FaqCoordTransformation

它非常详细地解释了从图形单元到图像像素的计算。

希望这是您正在寻找的。​​p>

【讨论】:

是的,安妮,这正是我所需要的。非常感谢! 链接似乎失效了。我试图在新网站 (graphviz.org/documentation) 上找到它来编辑问题,但找不到。你知道那个页面在哪里吗? 我已经修复了答案中的链接。【参考方案2】:

我刚刚找到了一个非常适合我的需求的类似解决方案

pos = nx.drawing.nx_agraph.graphviz_layout(G, prog='dot', args='-Grankdir=LR')

干杯!

【讨论】:

【参考方案3】:

使用 pydotplus,您可以加载并解析 dot/gv 文件并询问 pydotplus 生成的数据结构,但这种内部表示似乎最初并不具备所有节点属性,如 pos,除非它们已经在文件中。 但是您也可以调用 .write_dot() 来生成更详细的点文件版本。如果您对此进行解析,则生成的数据结构似乎具有所有节点的 pos(甚至是样条的 pos)

注意:最好按名称而不是索引来索引节点,因为详细文件中任何带有方括号的文本都将被解析为节点,因此节点列表可能包含虚假的额外元素。

在 Spyder 提示下的以下(略微编辑)实验中,我有一个简洁的点文件interior.gv(没有节点的 pos),我使用 .graph_from_dot_file(),然后是 .write_dot()。然后 .graph_from_dot_file() 再次 在详细生成的文件上,然后根据需要找到 pos。

import pydotplus as pdp

interior = pdp.graphviz.graph_from_dot_file('interior.gv')

interior.write_dot('interior2.dot')
Out[210]: True

interior2 = pdp.graphviz.graph_from_dot_file('interior2.dot')

interior2.get_nodes()[3].get_pos()
Out[214]: '"213.74,130"'

【讨论】:

【参考方案4】:

Networkx 可以做到这一点:

import networkx as nx

def setup_europe():
    G = nx.Graph()

    G.add_edge("Portugal", "Spain")
    G.add_edge("Spain","France")
    G.add_edge("France","Belgium")
    G.add_edge("France","Germany")
    G.add_edge("France","Italy")
    G.add_edge("Belgium","Netherlands")
    G.add_edge("Germany","Belgium")
    G.add_edge("Germany","Netherlands")
    G.add_edge("England","Wales")
    G.add_edge("England","Scotland")
    G.add_edge("Scotland","Wales")
    G.add_edge("Switzerland","Austria")
    G.add_edge("Switzerland","Germany")
    G.add_edge("Switzerland","France")
    G.add_edge("Switzerland","Italy")
    G.add_edge("Austria","Germany")
    G.add_edge("Austria","Italy")
    G.add_edge("Austria","Czech Republic")
    G.add_edge("Austria","Slovakia")
    G.add_edge("Austria","Hungary")
    G.add_edge("Denmark","Germany")
    G.add_edge("Poland","Czech Republic")
    G.add_edge("Poland","Slovakia")
    G.add_edge("Poland","Germany")
    G.add_edge("Czech Republic","Slovakia")
    G.add_edge("Czech Republic","Germany")
    G.add_edge("Slovakia","Hungary")
    return G

G = setup_europe()

写一个点文件:

nx.write_dot(G, '/tmp/out.dot')

计算节点的位置:

pos = nx.pygraphviz_layout(G, prog = 'dot')
print(pos)
# 'Netherlands': (713.86, 167.0), 'Italy': (473.86, 389.0), 'Czech Republic': (100.86, 241.0), 'Portugal': (879.86, 315.0), 'England': (1024.9, 241.0), 'Denmark': (568.86, 167.0), 'Poland': (100.86, 167.0), 'Scotland': (1024.9, 389.0), 'France': (571.86, 315.0), 'Belgium': (713.86, 19.0), 'Austria': (320.86, 167.0), 'Slovakia': (156.86, 315.0), 'Wales': (990.86, 315.0), 'Switzerland': (473.86, 241.0), 'Hungary': (294.86, 241.0), 'Germany': (465.86, 93.0), 'Spain': (879.86, 241.0)

渲染一个 png:

agraph = nx.to_agraph(G)
agraph.draw("/tmp/europe.png", format = 'png', prog = 'dot')

【讨论】:

感谢 unutbu,这当然解决了我的问题,尽管只是部分解决了。我实际上需要 gv 提供的布局中的坐标,而不是 networkx 提供的坐标。无论如何,感谢您的回答,它也对我帮助很大。任何想法如何用 gv 做到这一点? @Kiril:嗯,抱歉。我不知道。【参考方案5】:

仅使用 pydot/dot,您可以通过生成 SVG 然后从 SVG 读取节点的位置来实现。它有点hacky,但足够可靠

from xml.dom import minidom
import pydot
from io import BytesIO
def extract_node_positions(
    in_dot: pydot.Dot
) -> Dict[str, Tuple[str, float, float]]:
    """
    Extract the x,y positions from a pydot graph by rendering
    Args:
        in_dot: the graph to render

    Returns:
        a list of all the nodes

    Examples:
        >>> g = pydot.Dot()
        >>> g.add_node(pydot.Node("A"))
        >>> g.add_node(pydot.Node("B"))
        >>> g.add_node(pydot.Node("C"))
        >>> g.add_edge(pydot.Edge("A", "B"))
        >>> g.add_edge(pydot.Edge("B", "C"))
        >>> extract_node_positions(g)
        'A': ('A', 27.0, -157.8), 'B': ('B', 27.0, -85.8), 'C': ('C', 27.0, -13.8)
    """
    node_mapping = f'nodei': k.get_name() 
                    for i, k in enumerate(in_dot.get_nodes(), 1)
    svg_io = BytesIO(in_dot.create_svg())
    doc = minidom.parse(svg_io)  # parseString also exists
    node_dict = node_mapping[p.getAttribute('id')]: (c_text.firstChild.data,
                                        float(c_text.getAttribute('x')),
                                        float(c_text.getAttribute('y')))

                 for p in doc.getElementsByTagName("g") if "node" == p.getAttribute('class').lower()
                 for c_text in p.getElementsByTagName('text')
                 
    doc.unlink()
    return node_dict

【讨论】:

【参考方案6】:

要在 Python 中以二进制数据字符串的形式直接访问 Graphviz 渲染命令(例如点)的结果,而不是写入文件,请使用 Graph 或 Digraph 对象的 pipe() 方法:

h = Graph('hello', format='svg')

h.edge('Hello', 'World')

打印(h.pipe().decode('utf-8'))

【讨论】:

【参考方案7】:

我最近在这个问题上苦苦挣扎,这里的答案有些帮助,但并不完全在那里。关于networkx的建议是正确的。 Networkx 使用 pygraphviz 与 graphviz 交互,因此如果您愿意,可以直接使用 pygraphviz:

import pygraphviz as pgv
G = pgv.AGraph(strict=False,directed=True)
# add nodes and edges
G.add_edge(1,2)
G.add_edge(2,1)
# generate a layout--this creates `pos` attributes for both nodes and edges
G.layout(prog="dot") 
#change something about the graph that would change the layout, e.g.,
edge = G.get_edge("1", "2")
edge.attr["label"] = "test label"
# create the graph using the layout already generated; note, do not provide `prog`
G.draw("test.pdf")
# compare it to a newly generated layout
G.draw("test2.pdf", prog="dot")

如果你想使用layout 命令已经生成的节点和边缘位置,重要的部分是draw 命令提供prog。我现在看到这在 draw 的 pygraphviz 文档字符串中有所说明。顺便说一句,它与带有-n2 参数的prog="neato" 相同。查看源代码,pygraphviz 调用 graphviz 来生成布局。

【讨论】:

以上是关于如何从graphviz的布局中获取坐标?的主要内容,如果未能解决你的问题,请参考以下文章

Graphviz 网格对齐

如何垂直而不是水平地制作点(graphviz)布局未连接的节点?

将点文件(graphviz)转换为图像时如何设置分辨率?

流程图 Graphviz - Graph Visualization Software

doxygen+graphviz轻松绘制函数调用图(call graph)

doxygen+graphviz轻松绘制函数调用图(call graph)