所以,我使用viz.js从点文件生成了一个svg图 现在,使用javascript很容易选择它的元素,但我没有看到与原始点文件的任何关联。我在viz.js库中看不到任何对象结构,它将生成的svg图表元素与点源元素联系起来,所以,如果我用鼠标选择svg元素,我会知道这个svg元素是对应的到点元素,它是由它生成的。有没有办法得到这样的反馈?我需要这个,所以,如果我在svg中编辑一个元素(在浏览器中可视化),我就能将编辑映射回点文件并反映源上的变化。
digraph DB {
rankdir=LR
node [shape=record]
person [
label="
Person table|
<id> Person ID|
<fn> First Name|
<mn> Middle Name|
<ln> Last Name
"
]
address [
label="
Addresses table|
<id> Address ID|
<pid> Person ID|
<index> ZIP Code|
<street> Street Name|
<house> House Number|
<town> City/Town/Village Name|
<state> State Name|
<district> County/District Name|
<country> Country Name
"
]
phone [
label="
Phone Number table|
<pid> Person ID|
<cc> Country Code|
<ac> Area Code|
<n> Phone Number
"
]
{phone:pid address:pid} -> person:id
}
这是由Viz.js库生成的svg结果(但是,对我来说,我不在乎,如果其他库可以完成相同的操作,我将使用其他库):
<svg width="671pt" height="257pt" viewBox="0 0 671 257" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 253)">
<title>DB</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-253 666.8861,-253 666.8861,4 -4,4"></polygon>
<!-- person -->
<g id="node1" class="node">
<title>person</title>
<polygon fill="none" stroke="#000000" points="277.8566,-62.5 277.8566,-186.5 371.2234,-186.5 371.2234,-62.5 277.8566,-62.5"></polygon>
<text text-anchor="middle" x="324.54" y="-169.9" font-family="Times,serif" font-size="14.00" fill="#000000">Person table</text>
<polyline fill="none" stroke="#000000" points="277.8566,-161.7 371.2234,-161.7 "></polyline>
<text text-anchor="middle" x="324.54" y="-145.1" font-family="Times,serif" font-size="14.00" fill="#000000">Person ID</text>
<polyline fill="none" stroke="#000000" points="277.8566,-136.9 371.2234,-136.9 "></polyline>
<text text-anchor="middle" x="324.54" y="-120.3" font-family="Times,serif" font-size="14.00" fill="#000000">First Name</text>
<polyline fill="none" stroke="#000000" points="277.8566,-112.1 371.2234,-112.1 "></polyline>
<text text-anchor="middle" x="324.54" y="-95.5" font-family="Times,serif" font-size="14.00" fill="#000000">Middle Name</text>
<polyline fill="none" stroke="#000000" points="277.8566,-87.3 371.2234,-87.3 "></polyline>
<text text-anchor="middle" x="324.54" y="-70.7" font-family="Times,serif" font-size="14.00" fill="#000000">Last Name</text>
</g>
<!-- address -->
<g id="node2" class="node">
<title>address</title>
<polygon fill="none" stroke="#000000" points="504.1939,-.5 504.1939,-248.5 662.8861,-248.5 662.8861,-.5 504.1939,-.5"></polygon>
<text text-anchor="middle" x="583.54" y="-231.9" font-family="Times,serif" font-size="14.00" fill="#000000">Addresses table</text>
<polyline fill="none" stroke="#000000" points="504.1939,-223.7 662.8861,-223.7 "></polyline>
<text text-anchor="middle" x="583.54" y="-207.1" font-family="Times,serif" font-size="14.00" fill="#000000">Address ID</text>
<polyline fill="none" stroke="#000000" points="504.1939,-198.9 662.8861,-198.9 "></polyline>
<text text-anchor="middle" x="583.54" y="-182.3" font-family="Times,serif" font-size="14.00" fill="#000000">Person ID</text>
<polyline fill="none" stroke="#000000" points="504.1939,-174.1 662.8861,-174.1 "></polyline>
<text text-anchor="middle" x="583.54" y="-157.5" font-family="Times,serif" font-size="14.00" fill="#000000">ZIP Code</text>
<polyline fill="none" stroke="#000000" points="504.1939,-149.3 662.8861,-149.3 "></polyline>
<text text-anchor="middle" x="583.54" y="-132.7" font-family="Times,serif" font-size="14.00" fill="#000000">Street Name</text>
<polyline fill="none" stroke="#000000" points="504.1939,-124.5 662.8861,-124.5 "></polyline>
<text text-anchor="middle" x="583.54" y="-107.9" font-family="Times,serif" font-size="14.00" fill="#000000">House Number</text>
<polyline fill="none" stroke="#000000" points="504.1939,-99.7 662.8861,-99.7 "></polyline>
<text text-anchor="middle" x="583.54" y="-83.1" font-family="Times,serif" font-size="14.00" fill="#000000">City/Town/Village Name</text>
<polyline fill="none" stroke="#000000" points="504.1939,-74.9 662.8861,-74.9 "></polyline>
<text text-anchor="middle" x="583.54" y="-58.3" font-family="Times,serif" font-size="14.00" fill="#000000">State Name</text>
<polyline fill="none" stroke="#000000" points="504.1939,-50.1 662.8861,-50.1 "></polyline>
<text text-anchor="middle" x="583.54" y="-33.5" font-family="Times,serif" font-size="14.00" fill="#000000">County/District Name</text>
<polyline fill="none" stroke="#000000" points="504.1939,-25.3 662.8861,-25.3 "></polyline>
<text text-anchor="middle" x="583.54" y="-8.7" font-family="Times,serif" font-size="14.00" fill="#000000">Country Name</text>
</g>
<!-- address->person -->
<g id="edge1" class="edge">
<title>address->person:id</title>
<path fill="none" stroke="#000000" d="M503.9959,-133.8802C457.4691,-139.3669 403.6776,-145.7102 381.6916,-148.3029"></path>
<polygon fill="#000000" stroke="#000000" points="381.0613,-144.8529 371.54,-149.5 381.8811,-151.8047 381.0613,-144.8529"></polygon>
</g>
<!-- phone -->
<g id="node3" class="node">
<title>phone</title>
<polygon fill="none" stroke="#000000" points="0,-62.5 0,-186.5 131.08,-186.5 131.08,-62.5 0,-62.5"></polygon>
<text text-anchor="middle" x="65.54" y="-169.9" font-family="Times,serif" font-size="14.00" fill="#000000">Phone Number table</text>
<polyline fill="none" stroke="#000000" points="0,-161.7 131.08,-161.7 "></polyline>
<text text-anchor="middle" x="65.54" y="-145.1" font-family="Times,serif" font-size="14.00" fill="#000000">Person ID</text>
<polyline fill="none" stroke="#000000" points="0,-136.9 131.08,-136.9 "></polyline>
<text text-anchor="middle" x="65.54" y="-120.3" font-family="Times,serif" font-size="14.00" fill="#000000">Country Code</text>
<polyline fill="none" stroke="#000000" points="0,-112.1 131.08,-112.1 "></polyline>
<text text-anchor="middle" x="65.54" y="-95.5" font-family="Times,serif" font-size="14.00" fill="#000000">Area Code</text>
<polyline fill="none" stroke="#000000" points="0,-87.3 131.08,-87.3 "></polyline>
<text text-anchor="middle" x="65.54" y="-70.7" font-family="Times,serif" font-size="14.00" fill="#000000">Phone Number</text>
</g>
<!-- phone->person -->
<g id="edge2" class="edge">
<title>phone->person:id</title>
<path fill="none" stroke="#000000" d="M131.1663,-132.2389C180.2951,-138.0324 243.0276,-145.4301 267.307,-148.2933"></path>
<polygon fill="#000000" stroke="#000000" points="267.1989,-151.8047 277.54,-149.5 268.0187,-144.8529 267.1989,-151.8047"></polygon>
</g>
</g>
</svg>
假设我想在源点文件中编辑“城市/城镇/村庄名称”,而不是编辑源文本,而是通过直观地点击该点源的相关生成的svg表示。我可以写一些JavaScript,这样我就可以点击svg图形上的“城市/城镇/村庄名称”,然后该块变为活动状态。然后,我按照我的意愿就地编辑它。问题在于将更改保存回源。 JavaScript应相应地更改点源,但问题是使用viz.js生成的svg与源没有任何关系。即,如果你查看生成的svg的源代码,它不会添加任何id或任何东西,这表明从哪个dot元素生成了特定的svg元素。无法识别编辑哪个元素,以便将编辑后的值传递回正确的点元素,以便在源中进行更改。我可以想到一些方法来解决我的问题:
,但上面的工作太难了,需要很长时间才能完成,所以,我问,如果viz.js中有一些功能,我错过了,这样我就能完成任务或者,也许,我可以使用其他一些库,可以做什么,我需要什么?
答案 0 :(得分:3)
在比您更简单的情况下,SVG <title>
元素可用于引用节点和边缘。对于节点,标题是&#34; node_id&#34; (不要与节点属性id混淆),对于边缘,它是&#34; node_id edgeop node_id&#34;,例如a -> b
。从您的SVG代码:
<g id="node1" class="node">
<title>person</title>
person
可用于引用DOT源代码行:person [...
。
在一般情况下,Graphviz id属性是您的朋友:
id
允许图表作者为要包含在输出中的图形对象提供id。正常&#34; \ N&#34;,&#34; \ E&#34;,&#34; \ G&#34;替换适用。如果提供,提供商有责任保持其价值足以满足其预期的下游用途。特别注意,&#34; \ E&#34;不为多边提供唯一ID。如果未提供id属性,则使用唯一的内部标识。但是,图形编写器无法预测此值。外部提供的ID不在内部使用。
如果图形提供了id属性,则它将用作内部生成的属性的前缀。通过使这些不同,用户可以在同一文档中包括多个图像映射。
在您的情况下,您不仅要引用节点,还要引用record-based nodes的各个字段。
尽管记录标签的字段是使用 fieldId 定义的,但它们似乎并不打算传播到生成的SVG:
fieldId中的第一个字符串为字段分配端口名称,并且可以与节点名称组合以指示将边缘附加到节点的位置。 (见portPos。)
救援来HTML-like labels:
基于记录的形状在很大程度上被取代,并且被类似HTML的标签大大推广。也就是说,不是使用shape = record,而是可以考虑使用shape = none,margin = 0和类似HTML的标签。
使用它们,您可以创建一个节点,该节点是包含行和列的表,您可以 使用 ID 属性:
ID =&#34;值&#34;
允许用户为表或单元格指定唯一的ID。有关更多信息,请参阅id属性。注意&#34;值&#34;被视为与id属性类似的escString。
不幸的是,Graphviz中有a bug(更好地描述了here)导致在SVG输出中忽略此属性。幸运的是,有workaround。
以下是基于d3-graphviz的解决方案,该解决方案在内部使用viz.js。但是,您不需要使用d3-graphviz。您可以直接使用viz.js实现相同的功能。
如果你保持你的id足够独特并且你可以控制DOT源的格式,你可以使用简单的模式替换,如所提出的解决方案。
如果您无法控制DOT源的格式,您可能最好将信息反馈给生成它的应用程序。另一种方法是避免编写成熟的DOT解析器,使用&#39; dot&#39;来使用viz.js对DOT源进行标准化。作为输出格式并尝试解析它。
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/viz.js@1.8.0/viz.js"></script>
<script src="https://unpkg.com/d3-graphviz@0.1.2/build/d3-graphviz.js"></script>
<div id="graph" style="text-align: center;"></div>
<script>
var dotSrc = `
digraph DB {
graph [label="Click on a cell to convert to upper/lower case" labelloc="t", fontsize="20.0" tooltip=" "]
rankdir=LR
node [shape=plain]
person [
// NOTE: The use of HREF is a workaround for '[Dot] ID="value" fails to produce id string in svg:svg output for html nodes'
// See https://gitlab.com/graphviz/graphviz/issues/207
// For the workaorund and more info, see http://ftp.graphviz.org/mantisbt/view.php?id=2197
label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD>Person table</TD></TR>
<TR><TD ID="p.id" PORT="id" HREF=" ">Person ID</TD></TR>
<TR><TD ID="p.fn" PORT="fn" HREF=" ">First Name</TD></TR>
<TR><TD ID="p.mn" PORT="mn" HREF=" ">Middle Name</TD></TR>
<TR><TD ID="p.ln" PORT="ln" HREF=" ">Last Name</TD></TR>
</TABLE> >
]
address [
label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD>Addresses table</TD></TR>
<TR><TD ID="a.id" PORT="id" HREF=" ">Address ID</TD></TR>
<TR><TD ID="a.pid" PORT="pid" HREF=" ">Person ID</TD></TR>
<TR><TD ID="a.index" PORT="index" HREF=" ">ZIP Code</TD></TR>
<TR><TD ID="a.street" PORT="street" HREF=" ">Street Name</TD></TR>
<TR><TD ID="a.house" PORT="house" HREF=" ">House Number</TD></TR>
<TR><TD ID="a.town" PORT="town" HREF=" ">City/Town/Village Name</TD></TR>
<TR><TD ID="a.state" PORT="state" HREF=" ">State Name</TD></TR>
<TR><TD ID="a.district" PORT="district" HREF=" ">County/District Name</TD></TR>
<TR><TD ID="a.country" PORT="country" HREF=" ">Country Name</TD></TR>
</TABLE> >
]
phone [
label=< <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
<TR><TD>Phone Number table</TD></TR>
<TR><TD ID="n.pid" PORT="pid" HREF=" ">Person ID</TD></TR>
<TR><TD ID="n.cc" PORT="cc" HREF=" ">Country Code</TD></TR>
<TR><TD ID="n.ac" PORT="ac" HREF=" ">Area Code</TD></TR>
<TR><TD ID="n.n" PORT="n" HREF=" ">Phone Number</TD></TR>
</TABLE> >
]
{phone:pid address:pid} -> person:id
}
`;
var graphviz = d3.select("#graph").graphviz();
var dotSrcLines;
function render(dotSrc) {
// console.log('DOT source =', dotSrc);
dotSrcLines = dotSrc.split('\n');
transition1 = d3.transition()
.delay(100)
.duration(1000);
graphviz
.transition(transition1)
.renderDot(dotSrc);
transition1
.transition()
.duration(0)
.on("end", function () {
nodes = d3.selectAll('.node,.edge');
nodes
.selectAll("g")
.on("click", fieldClickHandler)
.selectAll("a")
// Remove the workaround attributes to avoid consuming the click events
.attr("href", null)
.attr("title", null);
});
}
function fieldClickHandler () {
var node = d3.select(this);
var text = node.selectAll('text').text();
var id = node.attr('id');
var class1 = node.attr('class');
dotElement = id.replace(/^a_/, '');
console.log('Element id="%s" class="%s" text="%s" dotElement="%s"', id, class1, text, dotElement);
console.log('Finding and deleting references to %s "%s" from the DOT source', class1, dotElement);
for (i = 0; i < dotSrcLines.length; i++) {
if (dotSrcLines[i].indexOf(dotElement) >= 0) {
ucText = text.toUpperCase();
lcText = text.toLowerCase();
if (text != ucText) {
newText = ucText;
} else {
newText = lcText;
}
console.log('Converting "%s" to "%s" on line %d: %s', text, newText, i, dotSrcLines[i]);
dotSrcLines[i] = dotSrcLines[i].replace(text, newText);
}
}
dotSrc = dotSrcLines.join('\n');
render(dotSrc);
}
render(dotSrc);
</script>
&#13;
答案 1 :(得分:0)
有一个undocumented feature,graphviz接受class
属性并将它们输出为svg class="foo"
。示例:
$ cat test.dot
digraph G {
graph [class="cats"];
subgraph cluster_big {
graph [class="big_cats"];
"Lion" [class="yellow social"];
"Snow Leopard" [class="white solitary"];
};
}
$ dot -Tsvg ~/test.dot | grep "<g"
<g id="graph0" class="graph cats" ...>
<g id="clust1" class="cluster big_cats">
<g id="node1" class="node yellow social">
<g id="node2" class="node white solitary">