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

时间:2012-12-18 17:58:46

标签: python coordinates graphviz graph-layout pygraph

我在某个项目上一直在使用pygraph。我完成了this example,它运行正常。

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

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

提前致谢!

7 个答案:

答案 0 :(得分:4)

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

gv.render(gvv)

然后找出节点获取其属性pos的位置:

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

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

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

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

希望这就是你要找的东西。

答案 1 :(得分:2)

使用pydotplus可以加载并解析点/ gv文件并询问pydotplus生成的数据结构,但是这种内部表示似乎并不最初拥有所有节点属性(如pos),除非它们已经存在于文件中。 br /> 但是,您也可以调用.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"'

答案 2 :(得分:1)

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')

enter image description here

答案 3 :(得分:1)

我刚刚找到了一种类似的解决方案,完全可以满足我的需求

Get-AzureStorageContainer : Method not found: 'Void Microsoft.WindowsAzure.Storage.OperationContext.set_StartTime(System.DateTimeOffset)'.
At line:3 char:1
+ Get-AzureStorageContainer -Context $context
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Get-AzStorageContainer], StorageException
    + FullyQualifiedErrorId : StorageException,Microsoft.WindowsAzure.Commands.Storage.Blob.Cmdlet.GetAzureStorageContainerCommand

Get-AzureStorageBlob : Method not found: 'Void Microsoft.WindowsAzure.Storage.OperationContext.set_StartTime(System.DateTimeOffset)'.
At line:5 char:9
+ $List = Get-AzureStorageBlob -Context $context -Container "dbbackup"  ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Get-AzStorageBlob], StorageException
    + FullyQualifiedErrorId : StorageException,Microsoft.WindowsAzure.Commands.Storage.Blob.Cmdlet.GetAzureStorageBlobCommand

欢呼!

答案 4 :(得分:1)

仅使用pydot / dot即可通过生成SVG,然后从SVG读取节点的位置来实现。它有点笨拙,但可以可靠地工作

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'node{i}': 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

答案 5 :(得分:1)

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

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

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

print(h.pipe()。decode('utf-8'))

答案 6 :(得分:0)

我最近为此苦苦挣扎,这里的答案有一些帮助,但还不够。关于 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")

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