【问题标题】:How to get the coordinates from layout from graphviz?如何从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 堆栈提出了类似的问题,但尚未得到答案。贝尔菲戈尔,你现在有办法解决吗?

标签: python coordinates graphviz graph-layout pygraph


【解决方案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'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
      

      【讨论】:

        【解决方案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 来生成布局。

          【讨论】:

            猜你喜欢
            • 2017-09-13
            • 1970-01-01
            • 1970-01-01
            • 2019-12-08
            • 2020-02-16
            • 2020-06-11
            • 2021-04-24
            • 2011-04-06
            • 1970-01-01
            相关资源
            最近更新 更多