8

我一直在使用 pygraph 进行一些项目。我完成了这个例子,它工作正常。

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

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

提前致谢!

4

7 回答 7

7

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

gv.render(gvv)

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

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

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

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

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

希望这是您正在寻找的。

于 2012-12-20T08:55:35.200 回答
2

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

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

干杯!

于 2019-09-11T17:15:57.180 回答
2

使用 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"'
于 2020-09-22T22:40:34.510 回答
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')

在此处输入图像描述

于 2012-12-18T20:43:26.967 回答
1

仅使用 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'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
于 2020-02-11T09:17:26.287 回答
1

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

h = 图形('你好',格式='svg')

h.edge('你好', '世界')

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

于 2020-04-14T18:53:30.330 回答
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给命令。我现在看到这是在 pygraphviz 文档字符串中说明的。顺便说一句,它与论点的作用相同。查看源代码,pygraphviz 调用 graphviz 来生成布局。drawlayoutdrawprog="neato"-n2

于 2021-02-10T16:25:29.807 回答