如何在像素与Graphviz尺寸/坐标间进行转换?
解决PNG渲染图坐标与Graphviz JSON坐标的转换问题
1. 核心坐标系差异
- PNG像素坐标系:以左上角为原点(0,0),向右为x轴正方向,向下为y轴正方向,对应你提到的167×251尺寸。
- Graphviz JSON的bb(边界框)坐标系:以左下角为原点(0,0),向右为x轴正方向,向上为y轴正方向,对应117×180的实际绘制区域尺寸。JSON里的bb格式为
[x_min, y_min, x_max, y_max],通常x_min和y_min为0,宽度=x_max - x_min,高度=y_max - y_min。
2. 坐标转换公式
假设:
- PNG点击坐标为
(png_x, png_y) - JSON的bb参数为
bb_x0, bb_y0, bb_x1, bb_y1,计算得bb_width = bb_x1 - bb_x0,bb_height = bb_y1 - bb_y0 - PNG宽
png_width,高png_height
转换步骤:
- 反转PNG的y坐标(适配JSON的y轴方向):
png_y_reversed = png_height - png_y - 计算缩放比例:
scale_x = bb_width / png_width,scale_y = bb_height / png_height - 映射到JSON坐标系:
json_x = bb_x0 + png_x * scale_xjson_y = bb_y0 + png_y_reversed * scale_y
3. Python实现示例(基于Tkinter)
import json from tkinter import Tk, Canvas, PhotoImage # 加载Graphviz JSON数据 with open('graph.json', 'r') as f: graph_data = json.load(f) # 解析bb参数 bb = graph_data['bb'].split(',') bb_x0, bb_y0, bb_x1, bb_y1 = map(float, bb) bb_width = bb_x1 - bb_x0 bb_height = bb_y1 - bb_y0 # PNG图片尺寸(可替换为从图片文件读取的实际尺寸) png_width = 167 png_height = 251 def on_click(event): # 获取PNG点击坐标 png_x, png_y = event.x, event.y # 转换为JSON坐标系坐标 png_y_reversed = png_height - png_y json_x = bb_x0 + png_x * (bb_width / png_width) json_y = bb_y0 + png_y_reversed * (bb_height / png_height) # 匹配点击的节点 for obj in graph_data['objects']: if obj['type'] == 'node': node_bb = obj['bb'].split(',') nx0, ny0, nx1, ny1 = map(float, node_bb) if nx0 <= json_x <= nx1 and ny0 <= json_y <= ny1: print(f"点击节点: {obj['name']}") return print("未命中任何节点") # 展示图片并绑定点击事件 root = Tk() canvas = Canvas(root, width=png_width, height=png_height) canvas.pack() img = PhotoImage(file='graph.png') canvas.create_image(0, 0, anchor='nw', image=img) canvas.bind('<Button-1>', on_click) root.mainloop()
4. 关键注意事项
- 确保JSON文件通过
dot -Tjson input.dot > graph.json或-Tjson0生成,两种格式的bb与节点坐标逻辑一致。 - PNG的尺寸包含Graphviz自动添加的边距,而bb仅对应实际图形区域,因此直接按比例缩放即可完成映射。
- 节点的bb同样为
[x_min, y_min, x_max, y_max],判断转换后的坐标是否落在该矩形内即可识别点击节点。
内容的提问来源于stack exchange,提问作者PuzzleAndy
相关产品推荐
相关产品推荐

