I am using pydotplus to generate the graph and graphviz to store it in SVG:
For eg, I add node with (there is no attrbute to specify node ID):
g.add_node(pydotplus.Node(fn_name,
style="filled",
fillcolor='cornflowerblue',
shape='Mrecord',
fontname="Consolas",
fontsize=8.0))
resulting SVG file:
https://github.com/tarun27sh/gdb_graphs/edit/master/gallery/test.svg
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: Created by: Tarun Sharma (tarun27sh#gmail.com) Pages: 1 -->
<svg width="872pt" height="45pt"
viewBox="0.00 0.00 872.00 45.00" 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 41)">
<title>Created by: Tarun Sharma (tarun27sh#gmail.com)</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-41 868,-41 868,4 -4,4"/>
<!-- main -->
<g id="node1" class="node"><title>main</title>
<path fill="blue" stroke="black" d="M12,-0.5C12,-0.5 42,-0.5 42,-0.5 48,-0.5 54,-6.5 54,-12.5 54,-12.5 54,-24.5 54,-24.5 54,-30.5 48,-36.5 42,-36.5 42,-36.5 12,-36.5 12,-36.5 6,-36.5 0,-30.5 0,-24.5 0,-24.5 0,-12.5 0,-12.5 0,-6.5 6,-0.5 12,-0.5"/>
<text text-anchor="middle" x="27" y="-15.4" font-family="Courier New Bold" font-size="12.00" fill="white">main</text>
</g>
<!-- func1 -->
<g id="node2" class="node"><title>func1</title>
<path fill="blue" stroke="black" d="M102,-0.5C102,-0.5 132,-0.5 132,-0.5 138,-0.5 144,-6.5 144,-12.5 144,-12.5 144,-24.5 144,-24.5 144,-30.5 138,-36.5 132,-36.5 132,-36.5 102,-36.5 102,-36.5 96,-36.5 90,-30.5 90,-24.5 90,-24.5 90,-12.5 90,-12.5 90,-6.5 96,-0.5 102,-0.5"/>
<text text-anchor="middle" x="117" y="-15.4" font-family="Courier New Bold" font-size="12.00" fill="white">func1</text>
</g>
<!-- main->func1 -->
<g id="edge1" class="edge"><title>main->func1</title>
<path fill="none" stroke="black" d="M54.4029,-18.5C62.3932,-18.5 71.3106,-18.5 79.8241,-18.5"/>
<polygon fill="black" stroke="black" points="79.919,-22.0001 89.919,-18.5 79.919,-15.0001 79.919,-22.0001"/>
</g>
As you can see generated SVG nodes are created with id=nodex, where x=numbers that keeps incrementing:
<g id="node1" class="node"><title>main</title>
How can I relate id of graph node with its title ? Javascript doesn't let me read svg node's title.
To add Javascript, I am adding <script> tags under <svg> tags:
<svg> ....
<script type="text/javascript">
window.addEventListener('load',function(){
alert('Hi')
})
function nodeClick() {
alert('You have clicked a node');
var node1 = document.getElementById('node1');
alert(node1.id);
alert(node1.title); <= error
}
</script>
Edit:
With suggested ans, I can pass id attribute while creating node or egde and use those in javascript (pydotplus allows all attributes that graphviz allows):
g.add_node(pydotplus.Node(elem,
id=elem,
style="filled",
fillcolor='cornflowerblue',
shape='box',
fontname="Consolas",
fontsize=12.0))
g.add_edge(pydotplus.Edge(edge_tuple,id="{}__{}".format(
edge_tuple[0],
edge_tuple[1])))
I know nothing about pydotplus, but the Graphviz language includes an id attribute for nodes, edges, clusters, and graphs.
Related
I'm new at coding.
I'm studying the way to make an animated portfolio like Sean Halpin or Stephanie Walter one. I want to put a face, in which, blinking eyes and a moving the mouth would be the animated elements. Basically, I want to play with the visibility of the closed eyes and open mouth. As an example, I drew a face as follows:
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 429 429">
<defs>
<style>
.cls-1 {
fill: #fff;
}
.cls-2 {
fill: none;
stroke: #000;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="face">
<path class="cls-1" d="M611,608.5a214,214,0,1,1,151.32-62.68A212.6,212.6,0,0,1,611,608.5Z" transform="translate(-396.46 -180)" />
<path d="M611,181a212.9,212.9,0,1,1-83.1,16.78A212.11,212.11,0,0,1,611,181m0-1c-118.46,0-214.5,96-214.5,214.5S492.5,609,611,609s214.5-96,214.5-214.5S729.43,180,611,180Z" transform="translate(-396.46 -180)" />
</g>
<g id="eyes">
<g id="eye_r">
<circle class="cls-1" cx="319.15" cy="128.63" r="48.5" />
<path d="M715.61,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_r">
<circle cx="319.15" cy="128.63" r="19" />
</g>
<g id="eye_l">
<circle class="cls-1" cx="109.85" cy="128.63" r="48.5" />
<path d="M506.32,260.62a48,48,0,1,1-48,48,48.06,48.06,0,0,1,48-48m0-1a49,49,0,1,0,49,49,49,49,0,0,0-49-49Z" transform="translate(-396.46 -180)" />
</g>
<g id="iris_l">
<circle cx="109.85" cy="128.63" r="19" />
</g>
<line id="closed_eye_l" class="cls-2" x1="62.04" y1="128.5" x2="158.04" y2="128.5" />
<line id="closed_eye_r" class="cls-2" x1="270.69" y1="128.23" x2="366.69" y2="128.23" />
</g>
<g id="closed_mouth">
<ellipse cx="214.5" cy="309" rx="108.5" ry="11.5" />
<path d="M611,478c29.08,0,56.41,1.25,77,3.51,30.68,3.38,31,7.32,31,7.49s-.35,4.11-31,7.49C667.37,498.75,640,500,611,500s-56.41-1.25-77-3.51c-30.69-3.38-31-7.32-31-7.49s.35-4.11,31-7.49c20.55-2.26,47.88-3.51,77-3.51m0-1c-60.2,0-109,5.37-109,12s48.8,12,109,12,109-5.37,109-12-48.8-12-109-12Z" transform="translate(-396.46 -180)" />
</g>
</svg>
So, I thought three ways to do this:
Place the svg inside an tag, calling then a function that takes into consideration the loading of the file. An example of what I'm saying is found in the following resource: https://www.petercollingridge.co.uk/tutorials/svg/interactive/javascript/, in the "External SVG + External JavaScript" part. It didn't work. The contentDocument ALWAYS returns "null".
In my example, it would be:
HTML:
<object id="face" data="path/to/face.svg" type="image/svg+xml"></object>
JS:
<script type="text/javascript">
window.addEventListener("load", function() {
var svgObject = document.getElementById('face').contentDocument;
var svg = svgObject.getElementById('closed_eye_r');
svg.style.visibility="hidden";
});
</script>
Inline SVG - call a "transform" property. Sean Halpin does it but I'm not sure what he does.
HTML: https://www.seanhalpin.design/
JS: https://www.seanhalpin.design/js/scripts.js
Inline SVG, use getElementById and apply functions to animate the internal parts of the SVG file.
Questions:
a. Is Inline SVG a good practice?
b. Which is the best way to animate an SVG?
I hope to have been clear. Let me know if something is not well presented, I want to learn to edit questions in order to make them as clear as possible.
Thanks!
I am following this tutorial : https://archive-2_1_4.lightningdesignsystem.com/resources/lightning-svg-icon-component-helper on how to use svg on lightning component but for some reason it is not working for my case:
I have already created the component svgIcon and added all the code into it.
In my case it does not seem to work since its showing a non sense blue square.
I uploaded the svg file into static resource and tried smth like this:
<c:svgIcon svgPath="{!$Resource.feedIcon}" category="standard" size="large" name="user" />
While this part on the tutorial looks as following:
<c:svgIcon svgPath="/resource/slds214/assets/icons/standard-sprite/svg/symbols.svg#user" category="standard" size="large" name="user" />
Dosnt specify where does they store the svg and also dont know what #user stands for in this case
The svg file I uploaded to salesforce static resources looks as below:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="82px" height="82px" viewBox="0 0 82 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 55.2 (78181) - https://sketchapp.com -->
<title>Group 4</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Icons-for-svg-export" transform="translate(-119.000000, -118.000000)" fill="#8B8D8E">
<g id="Group-4" transform="translate(120.000000, 119.000000)">
<g id="Group-3" stroke="#8B8D8E" stroke-width="0.4">
<g id="chat">
<path d="M68.2614576,11.7212036 C60.7001948,4.16069852 50.6693958,0 39.979772,0 C29.2899367,0 19.2593492,4.16069852 11.6980863,11.7212036 C-2.91552041,26.3339658 -3.9306097,49.72521 9.17822921,65.5056316 C7.8922944,68.0929603 5.8291323,71.1039699 2.76758412,72.6094747 C1.2957998,73.3363284 0.467405755,74.9094868 0.720913785,76.5331741 C0.974421815,78.1568613 2.22652736,79.4253669 3.85033192,79.6788567 C4.61106745,79.7976733 5.69381525,79.9158558 7.01273359,79.9158558 C10.5485257,79.9158558 15.758232,79.0866759 21.0861293,75.2481778 C27.0229533,78.4441778 33.5185944,80 39.9632802,80 C50.3144,80 60.5646663,75.9921564 68.2779494,68.2796421 C75.8390008,60.7189256 80,50.6899085 80,40 C80,29.3111486 75.8216633,19.2810744 68.2614576,11.7212036 Z M65.0303403,65.0489696 C53.6129644,76.4655204 35.9369638,78.7314944 22.0335601,70.5631637 C21.12017,70.0213003 19.9868898,70.1906458 19.2593492,70.9181338 C19.1916907,70.9521721 19.1238207,71.0024894 19.0734997,71.0528068 C14.4896347,74.6048341 9.99013149,75.3488126 7.01273359,75.3488126 L6.99624183,75.3488126 C10.429489,72.8458396 12.5949846,69.1253125 13.863582,66.2331196 C14.066769,65.7426308 14.1005982,65.2348057 13.9817729,64.7614417 C13.9314519,64.3047796 13.7456024,63.8481176 13.4233787,63.4755998 C1.04229177,49.4882108 1.68483631,28.1779502 14.9125005,14.9512418 C28.7317539,1.13298696 51.2112983,1.13298696 65.0130028,14.9512418 C78.8495937,28.7692852 78.8495937,51.2307148 65.0303403,65.0489696 Z" id="Fill-1"></path>
</g>
</g>
<path d="M40,39 C45.5228475,39 50,34.5228475 50,29 C50,23.4771525 45.5228475,19 40,19 C34.4771525,19 30,23.4771525 30,29 C30,34.5228475 34.4771525,39 40,39 Z M40,34 C37.2385763,34 35,31.7614237 35,29 C35,26.2385763 37.2385763,24 40,24 C42.7614237,24 45,26.2385763 45,29 C45,31.7614237 42.7614237,34 40,34 Z" id="Oval" fill-rule="nonzero"></path>
<path d="M53.3820358,52.1419451 L53.3820358,58.0287934 L58.3820358,58.0287934 L58.3820358,52.0610237 C58.3820358,50.6620146 57.7745968,49.3319332 56.7172836,48.4157917 L56.7172836,48.4157917 C47.0341731,40.0255636 32.5627654,40.3598339 23.2773818,49.188209 C22.7810083,49.660152 22.5,50.3150789 22.5,51 L22.5,58.0287934 L27.5,58.0287934 L27.5,52.1101962 C34.9103462,45.7645802 45.9109476,45.7203441 53.3820358,52.1419451 Z" id="Path-3" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>
Any idea how can I use svg icons in lightening component?
Add Svg in static resources and use like this.
"{!$Resource.delawareSVGs + '/Icons/exclamation-mark.svg'}"
This question and this question are similar, but the answers do not yield a clean, consistent method for finding the top most SVG element among different HTML strings.
The goal is to extract the top-most SVG element from a HTML string.
$(htmlString).find("svg") does not work.
$($.parseHTML(htmlString)) only generates an array of jQuery objects, but the goal is to turn the htmlString into one jQuery object where you can execute find and retrieve the top-most SVG element.
Example HTML string:
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424" style="enable-background:new 0 0 424 424;" xml:space="preserve" width="512px" height="512px">
<g>
<g>
<path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
<path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
<path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>
Result of $($.parseHTML(htmlString)), where htmlString is the string above:
$($.parseHTML(htmlString))
w.fn.init(6) [comment, text, comment, text, svg#Capa_1, text]
0: comment
1: text
2: comment
3: text
4: svg#Capa_1
5: text
length: 6
You cannot use find on the result because the markup you give to jQuery doesn't represent a tree: Doctype and comments nodes are siblings of your <svg>.
Thus, you need filter the jQuery entries in order to keep only the svg node:
const $svg = $(`<?xml version="1.0" encoding="utf-8"?>
<!-- comment1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424">
<g>
<g>
<path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
<path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
<path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
</g>
</g>
</svg>`)
.filter((i, el) => $(el).is('svg'));
console.log($svg.attr('id'));
$svg.find('path').attr('fill', 'red');
$('body').append($svg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Now, you may also want to use a native DOMParser, which might be better at handling namespaces than jQuery. From there, you will have an XMLDocument, whose documentElement will be your <svg> node.
You will then be able to work on it from jQuery:
const str = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424">
<g>
<g>
<path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
<path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
<path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
</g>
</g>
</svg>`;
const doc = (new DOMParser).parseFromString(str, 'image/svg+xml');
// use the second argument (context) of jQuery()
const $svg = $('svg', doc);
console.log($svg.attr('id'));
$svg.find('path').attr('fill', 'red');
$('body').append($svg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I want to use my own designed custom icons in react which I have in both formats SVG and TTF.
How can I do that? I want to put those icons in my navbar like a custom home icon for home button.
I'm not sure how is you webpack configured to resolve svg and include them in your build, But I can give you two different approaches here:
You can make separate SVG files generated from some tool in xml
myIcon.svg
<?xml version="1.0" encoding="UTF-8"?>
<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
<title>bus-start</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Booking-a-trip" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Verify" transform="translate(-113.000000, -312.000000)">
<g id="Group-3" transform="translate(63.000000, 144.000000)">
<g id="bus-start" transform="translate(51.000000, 169.000000)">
<circle id="Oval" stroke="#606C74" fill="#FFFFFF" cx="12" cy="12" r="12"></circle>
<path d="M6,15.0585702 C6,15.6952627 6.2925,16.2668389 6.75,16.6647717 L6.75,17.952627 C6.75,18.3505599 7.0875,18.6761413 7.5,18.6761413 L8.25,18.6761413 C8.6625,18.6761413 9,18.3505599 9,17.952627 L9,17.2291128 L15,17.2291128 L15,17.952627 C15,18.3505599 15.3375,18.6761413 15.75,18.6761413 L16.5,18.6761413 C16.9125,18.6761413 17.25,18.3505599 17.25,17.952627 L17.25,16.6647717 C17.7075,16.2668389 18,15.6952627 18,15.0585702 L18,7.82342808 C18,5.29112834 15.315,4.92937123 12,4.92937123 C8.685,4.92937123 6,5.29112834 6,7.82342808 L6,15.0585702 Z M8.625,15.7820844 C8.0025,15.7820844 7.5,15.2973299 7.5,14.6968131 C7.5,14.0962963 8.0025,13.6115418 8.625,13.6115418 C9.2475,13.6115418 9.75,14.0962963 9.75,14.6968131 C9.75,15.2973299 9.2475,15.7820844 8.625,15.7820844 Z M15.375,15.7820844 C14.7525,15.7820844 14.25,15.2973299 14.25,14.6968131 C14.25,14.0962963 14.7525,13.6115418 15.375,13.6115418 C15.9975,13.6115418 16.5,14.0962963 16.5,14.6968131 C16.5,15.2973299 15.9975,15.7820844 15.375,15.7820844 Z M16.5,11.4409991 L7.5,11.4409991 L7.5,7.82342808 L16.5,7.82342808 L16.5,11.4409991 Z" id="Shape" fill="#606C74" fill-rule="nonzero"></path>
</g>
</g>
</g>
</g>
</svg>
Later in your component you can import it like :
import myIcon from 'assets/myIcon.svg' // depending on your folder structure
in render method:
render(){
return (
<img src={myIcon} />
)
}
Second approach you can make your svg in react using <svg>:
here is the working codesandbox: Svg Icon + React
So, I've generated an svg graph from the dot file, using viz.js.
Now, it's easy to select it's elements, using javascript, but I don't see any association to the original dot file. I don't see any object structure in viz.js library, that ties the generated svg chart elements to the dot source elements, so, that, if I select the svg element with the mouse, I would know that this svg element is correspondent to the dot element, that it was generated from. Is there a way to have a such feedback? I need this, so that, if I edit an element in svg (visually in the browser), I would be able to map the edit back to the dot file and reflect the change on the source.
Explanation
So, here is the example of a possible source GraphViz dot code:
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
}
Here is the svg result, generated by Viz.js library (but, as for me, I don't care, if the same can be done by other library, I will use that other library):
<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>
Let's say, I want to edit the " City/Town/Village Name" in the source dot file not by editing source's text, but by visually clicking on the related generated svg representation of that dot source. I can write some JavaScript, that will allow me to click on "City/Town/Village Name" on the svg graphic, for example, and the block becomes active. Then, I edit it in-place, as I wish. The problem lies with saving the change back to the source. JavaScript should change the dot source accordingly, but the problem is that the svg, generated with viz.js doesn't have any ties to the source. I.e., if you look at the source of the generated svg, it doesn't add any ids or anything, that would indicate, that a particular svg element was generated from which dot element. There is no way to identify, which element was edited in order to pass the edited value back to the correct dot element for the change to be made in the source. There are some ways, that I can think of so solve my issue:
edit the viz.js library, so it puts some ids on the generated svg
tediously analyse the generated svg, in order to logically identify the correct source element, to the edited svg element
,but the above are too difficult jobs and would take a long time to accomplish, so, I am asking, if there is some feature in viz.js, that I've missed, that would allow me to accomplish my task, or maybe, there is some other library that I could use, that can do, what I require?
In cases simpler that yours, the SVG <title> element can be used to refer back to nodes and edges. For nodes, the title is the "node_id" (not to be confused with the node attribute id) and for edges it is "node_id edgeop node_id", e.g. a -> b. From your SVG code:
<g id="node1" class="node">
<title>person</title>
person can be used to refer back to the DOT source line: person [....
In the general case, the Graphviz id attribute is your friend:
id
Allows the graph author to provide an id for graph objects which is to be included in the output. Normal "\N", "\E", "\G" substitutions are applied. If provided, it is the responsibility of the provider to keep its values sufficiently unique for its intended downstream use. Note, in particular, that "\E" does not provide a unique id for multi-edges. If no id attribute is provided, then a unique internal id is used. However, this value is unpredictable by the graph writer. An externally provided id is not used internally.
If the graph provides an id attribute, this will be used as a prefix for internally generated attributes. By making these distinct, the user can include multiple image maps in the same document.
In your case, you want to reference not only nodes, but also individual fields of record-based nodes.
Although the fields of record labels are defined with fieldId's, they do not seem to be intended to propagate to the generated SVG:
The first string in fieldId assigns a portname to the field and can be combined with the node name to indicate where to attach an edge to the node. (See portPos.)
To your rescue comes HTML-like labels:
The record-based shape has largely been superseded and greatly generalized by HTML-like labels. That is, instead of using shape=record, one might consider using shape=none, margin=0 and an HTML-like label.
With them you can create a node that is a table with rows and columns where you can use the ID attribute:
ID="value"
allows the user to specify a unique ID for a table or cell. See the id attribute for more information. Note that the "value" is treated as an escString similarly to the id attribute.
Unfortunately there is a bug in Graphviz (better described here) that causes this attribute to be ignored in the SVG output. Fortunately, there's a workaround.
Below is a solution which is based on d3-graphviz, which uses viz.js internally. You don't need to use d3-graphviz, though. You can achieve the same thing with viz.js directly.
If you keep your id's sufficiently unique and you have control of the formatting of the DOT source, you can use simple pattern replacement as in the presented solution.
If you don't have control over the formatting of the DOT source, you are probably better off feeding back the information to the application that generates it. An alternative, to avoid writing a full-fledged DOT parser, is to normalize the DOT source with viz.js by using 'dot' as output format and try to parse that.
<!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>
Graphviz accepts class attributes and outputs them as SVG class="foo". Example:
$ 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">