Related
I built in Apex a D3 force graph basically like http://bl.ocks.org/mbostock/1093130 or http://bl.ocks.org/mbostock/4062045. The difference is, that I pull my data with an Application Process from a address table from the database. It works just fine.
The colors of the nodes are defined by the address type (like Contact, Payment Office, Licensees, ...). Now I want to add a legend on the side of the page with the different colors the graph is using and the connected address type.
Do I do that in the Page Attributes in the CSS Inline Part, or do I have to add something in the D3 graph JavaScript code.
Here is my code:
var graph;
function get_chart_data() {
var get = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=AddressData',$v('pFlowStepId'));
var data_all = get.get();
var obj = eval ("(" + data_all + ")");
return obj;
}
function showChart2() {
graph = get_chart_data();
var width = 1000,
height = 800;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(0)
.charge(-400)
.linkDistance(90)
.size([width, height]);
var svg = d3.select("#chart2").append("svg")
.attr("width", width)
.attr("height", height);
var nodeById = d3.map();
graph.nodes.forEach(function(node) {
nodeById.set(node.id, node);
});
graph.links.forEach(function(link) {
link.source = nodeById.get(link.source);
link.target = nodeById.get(link.target);
});
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 8)
.style("fill", function(d) { return color(d.type); })
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.first_name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
};
I hope I explained it well enough for you to understand it.
Guess what, I just solved my own question :)
I added a code in the JavaScript part of the Page Attributes at the end of the function showChart2(), but still in it.
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
And here is the full working code:
var graph;
function get_chart_data() {
var get = new htmldb_Get(null,$v('pFlowId'),'APPLICATION_PROCESS=AddressData',$v('pFlowStepId'));
var data_all = get.get();
var obj = eval ("(" + data_all + ")");
return obj;
}
function showChart2() {
graph = get_chart_data();
var width = 1000,
height = 800;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(0)
.charge(-400)
.linkDistance(90)
.size([width, height]);
var svg = d3.select("#chart2").append("svg")
.attr("width", width)
.attr("height", height);
var nodeById = d3.map();
graph.nodes.forEach(function(node) {
nodeById.set(node.id, node);
});
graph.links.forEach(function(link) {
link.source = nodeById.get(link.source);
link.target = nodeById.get(link.target);
});
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 8)
.style("fill", function(d) { return color(d.type); })
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.first_name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
};
I never thought I could answer my own question, but it works ;)
I hope it helps somebody else too...
I'm able to get images and labels to show for the nodes, but they show at the top left of the screen.
Nodes show up in the correct position when I use this
.enter().append("circle")
Labels and node images show at the top left (incorrect) when I use this:
.enter().append("g")
This works with append "circle" (commented out in the code below):
When I comment out append circle and use append "g" (in order to use node images and labels) the images and labels all show up near (0,0) instead of near the node:
Also, what exactly is append "g"? Where is the documentation to find out what's possible with append "g"?
Here is all the code:
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size(\[width, height\]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = getData();
var nodeMap = {};
graph.nodes.forEach(function(d) { nodeMap\[d.name\] = d; });
graph.links.forEach(function(l) {
l.source = nodeMap\[l.source\];
l.target = nodeMap\[l.target\];
})
force.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) {
return d.line_color;
})
.style("stroke-width", function(d) {
return Math.sqrt(d.value)+1;
});
var node = svg.selectAll(".node")
.data(graph.nodes)
// .enter().append("circle")
// .attr("class", "node")
// .attr("r", 10)
// .style("fill", function(d) { return d.fill_color; })
// .call(force.drag);
.enter().append("g")
.attr("class", "node")
.attr("r", 15)
.style("fill", function(d) { return d.fill_color; })
.on("click", function(d){
alert("You clicked on node " + d.name);
})
.call(force.drag);
node.append("title")
.text(function(d) { return d.label; });
node.append("image")
.attr("xlink:href", function(d) { return d.image_url })
.attr("x", -8)
.attr("y", -8)
.attr("width", 26)
.attr("height", 26);
node.append("text")
.attr("dx", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return 16;
}
})
.attr("dy", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return ".35em";
}
})
// .attr("dy", ".35em")
.text(function(d) { return d.name });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
function getData() {
return {
"nodes":\[
{"name":"user1","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user2","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user3","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"tag1","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag2","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag3","image_url":"","fill_color":"blue","text_color":"black"}
\],
"links":\[
{"source":"tag1","target":"user1","value":1,"line_color":"green"},
{"source":"tag2","target":"user1","value":1,"line_color":"green"},
{"source":"tag3","target":"user1","value":1,"line_color":"green"},
{"source":"tag1","target":"user2","value":1,"line_color":"green"},
{"source":"tag2","target":"user2","value":1,"line_color":"green"}
\]
};
}
</script>
With .append("g") you insert a SVG Group Element.
The problem is, that you try to apply attributes that are for circles, like the radius with .attr("r",15), to the group element.
You have to use circles if you want to draw a circle. Group elements do not have any shape. They are used to group elements like circles.
A solution would be to append the g element and transform it to the location of the node. I updated your code in the following snippet. I used the group elements and added the circle, image and text inside the group elements.
Moreover I removed the backslashes before each angular bracket and set the title to the field name instead of label.
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = getData();
var nodeMap = {};
graph.nodes.forEach(function(d) { nodeMap[d.name] = d; });
graph.links.forEach(function(l) {
l.source = nodeMap[l.source];
l.target = nodeMap[l.target];
})
force.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter()
.append("line")
.attr("class", "link")
.style("stroke", function(d) {
return d.line_color;
})
.style("stroke-width", function(d) {
return Math.sqrt(d.value)+1;
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"})
.call(force.drag);
node.append("circle")
.attr("class", "node")
.attr("r", 15)
.style("fill", function(d) { return d.fill_color; })
.on("click", function(d){
alert("You clicked on node " + d.name);
});
node.append("title")
.text(function(d) { return d.name; });
node.append("image")
.attr("xlink:href", function(d) { return d.image_url })
.attr("x", -8)
.attr("y", -8)
.attr("width", 26)
.attr("height", 26);
node.append("text")
.attr("dx", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return 16;
}
})
.attr("dy", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return ".35em";
}
})
// .attr("dy", ".35em")
.text(function(d) { return d.name });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d){return "translate("+d.x+","+d.y+")"});
});
function getData() {
return {
"nodes":[
{"name":"user1","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user2","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user3","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"tag1","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag2","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag3","image_url":"","fill_color":"blue","text_color":"black"}
],
"links":[
{"source":"tag1","target":"user1","value":1,"line_color":"green"},
{"source":"tag2","target":"user1","value":1,"line_color":"green"},
{"source":"tag3","target":"user1","value":1,"line_color":"green"},
{"source":"tag1","target":"user2","value":1,"line_color":"green"},
{"source":"tag2","target":"user2","value":1,"line_color":"green"}
]
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I don't understand how to display text on my nodes
I tried several times but didn't work, please help me to solve it
I'm not familiar with D3.js at all but I need to visualize a graph for my project, and I need this additional feature (display label) on the nodes
Here is the input file:
{
"nodes":[
{"name":"A","group":0},
{"name":"B","group":1},
{"name":"C","group":2},
{"name":"D","group":3},
{"name":"E","group":4}
],
"links":[
{"source":0,"target":1,"value":11},
{"source":0,"target":2,"value":11},
{"source":1,"target":2,"value":21},
{"source":1,"target":3,"value":21},
{"source":1,"target":4,"value":21},
{"source":2,"target":3,"value":21},
{"source":2,"target":4,"value":21},
{"source":3,"target":4,"value":11}
]
}
D3.js Code:
<script type="text/javascript">
// use print_graph() method to output graph vertices
main_graph();
var width = 700,
height = 300;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(150)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(0,0)");
d3.json("input.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
// size of nodes
.attr("r", 20)
.style("fill", function(d) {
return color(d.group);
})
.call(force.drag);
//I'm stuck here
var texts = svg.selectAll("text.label")
.data(graph.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "white")
.text(function(d) {
return d.name;
});
/*
// 1st trying
var text = svg.selectAll("text")
.data(graph.node)
.enter().append("text")
.attr("fill","white")
.text(function (d) {return d.name; });
*/
// another trying
//node.append("text")
//.text(function(d) { return d.name; });
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
});
</script>
How you are adding the text is fine but in your force.on("tick", function() {}) handler you don't position the them:
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
texts.attr("x", function(d) { //<-- NEED TO POSITION THESE TOO
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
Here's your code fixed.
I am making a timeline sort of project. As of right now, my code looks like the following:
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-300)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("input.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); })
.style("visibility", function(d) {
return d.value == 2 ? "hidden" : "visible";
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20)
.style("fill", function(d) { return color(d.group); })
.call(force.drag)
.style("visibility", function(d) {
return d.group == 1 ? "hidden" : "visible";
})
.on("mouseover", function(d) {
if(d.group == 2) {
node.filter(function(d) { return d.group == 1; }).style("visibility", "visible");
link.filter(function(d) { return d.value == 2; }).style("visibility", "visible");
texts.filter(function(d) { return d.value == 2; }).style("visibility", "visible");
}
}).on("mouseout", function(d) {
if(d.group == 2) {
node.filter(function(d) { return d.group == 1; }).style("visibility", "hidden");
link.filter(function(d) { return d.value == 2; }).style("visibility", "hidden");
}
});
var texts = svg.selectAll("text.label")
.data(graph.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "black")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
});
As of right now, the text, or labelsconstantly show, even when the nodes and links don't. I'm wondering what I need to do? I don't know if I can assign a value or group to the labels
I still not understanding why the code bellow does not display its labels / text...
I have defined the css and set the attribute like the title when the move is over the node:
Json:
{
"nodes":[
{"name":"t1","group":1},
{"name":"t2","group":1},
{"name":"t3","group":1},
{"name":"t4","group":1},
{"name":"hate","group":2},
{"name":"good","group":2},
{"name":"aiport","group":3},
{"name":"flight","group":3}
],
"links":[
{"source":0,"target":4,"value":4},
{"source":0,"target":5,"value":4},
{"source":1,"target":4,"value":4},
{"source":2,"target":5,"value":4},
{"source":3,"target":5,"value":4},
{"source":4,"target":6,"value":4},
{"source":5,"target":6,"value":4},
{"source":5,"target":7,"value":4}
]
}
Code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
fill: #555;
stroke: #999;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 1024,
height = 768;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("data.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll("line.link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll("circle.node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
node.append("text")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>
You are adding the text element inside the circle element - try running your code and have a look at the svg with the DOM inspector. I'm not sure text is allowed there. Instead add the text elements separately - like another rendering of the nodes:
var texts = svg.selectAll("text.label")
.data(graph.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "black")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
Another option would be to add both circle and text elements inside a g container element as shown below:
var container = svg.selectAll("g.node").data(graph.nodes).enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
}).call(force.drag);
container.append("circle")
.attr("r", 5)
.style("fill", color);
container.append("text")
.style("text-anchor", "middle")
.text(function (d) {
return d.name;
});
Here you can play with a working jsfiddle:
http://jsfiddle.net/vfu78/16/