I'm generating voronoi paths based on some points in forced layout. I'd like to randomly assign these paths 1 of 10 classes and then wrap some of these classes with a clipPath that I can then apply to another element.
Is it possible to wrap svg tags around elements using d3 as opposed to appending?
Or is it even possible to use multiple paths generated by d3 in a clipPath?
Thank you for your help,
w = $(window).width();
h = $(window).height();
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
$('#grid').css('height', h);
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
/*
var clip = gClip.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", "clip.jpg")
.attr("x", "0px")
.attr("y", "0px")
.attr("width", w)
.attr("height", h);
*/
var selectPath = d3.selectAll('.path-10');
console.log(selectPath);
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
There is a strange mix of jQuery and D3 in this. Try to do the things in D3 when you work with it. For example I'd rather do this:
.attr("class", function(d){return 'path-'+Math.floor((Math.random()*10)+1))});
than this:
$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
d3 has an exotic but smart way of doing things, it's better to learn this update pattern well before doing something serious.
And here is the working code:
w = 1200;
h = 500;
function ranNum(){
return Math.floor((Math.random()*10)+1);
}
var vertices = d3.range(50).map(function(d) { return {x: d.x, y: d.y}; });
//console.log(vertices);
links = [];
voronoiVertices = [];
var force = d3.layout.force()
.nodes(vertices)
.size([w, h])
.linkDistance(60)
.charge(-900)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
//path gradient
var defs = svg.append('defs')
var radialGradient = defs.append('radialGradient')
.attr('id', 'pathGrad')
.attr('cx', '50%')
.attr('cy', '50%')
.attr('r', '50%')
.attr('fx', '50%')
.attr('fy', '50%');
var stop1 = radialGradient.append('stop')
.attr('offset', '.2')
.attr('stop-color', '#a8a8a8');
var stop2 = radialGradient.append('stop')
.attr('offset', '1')
.attr('stop-color', '#0000000');
//path dropShadow
var filterShadow = defs.append('filter')
.attr('id', 'pathShadow')
.attr('height', '130%');
var gCir = svg.append('g')
.attr("class", "gCircle");
var gPath = svg.append('g')
.attr("class", "gPath");
var circle = svg.selectAll("circle");
var path = gPath.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("fill", "url(#pathGrad)");
//wraps path with random class after generation
//$('path').each(function(){$(this).attr('class', 'path-' + Math.floor((Math.random()*10)+1))});
var clip = defs.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("id", "clip-rect")
.attr("x", "0")
.attr("y", "0")
.attr("width", '900px')
.attr("height", '900px');
var gClip = svg.append("svg:g")
.attr('clip-path', 'url(#clip)');
function tick() {
voronoiVertices = vertices.map(function(t){return [t.x, t.y]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
path.attr("fill", "url(#pathGrad)")
.attr("d", function(t) { return "M" + t.join("L") + "Z"; });
circle = circle.data(vertices)
circle.enter().append("circle")
.call(force.drag)
.attr("r", 0)
.attr('class', function(d) { return d.index; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.transition().duration(5000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Good luck!
Related
This is currently the code looks like however I seem to be unable to print the metric/weight on the network links or the network title on the nodes. If someone could help out that would be great. if you'd like to see the network diagram please send it over a message. I'm using D3.js version 3.
Below is my code:
var width = 1000, height = 700;
var color = d3.scale.category10();
var svg = d3.select("#d3-example").select("svg")
if (svg.empty()) {
svg = d3.select("#d3-example").append("svg")
.attr("width", width)
.attr("height", height);
}
d3.json("graph.json", function(error, graph) {
links = graph.links
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
});
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.gravity(0.1)
.linkDistance(200)
.on('tick', tick)
.charge(-1000)
.start();
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
link.append("text")
.attr("class", "data text-tooltip1")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.y + d.target.y)/2) + "," +
((d.source.x + d.target.x)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d.target.name);
return d.target.name;
});
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.02)
node.append("svg:title")
.text(function(d) { return d.name });
function tick(e) {
node.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(force.drag);
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; });
};
})
Try the below modifications in your code. This should solve your problem:
var width = 1000, height = 700;
var color = d3.scale.category10();
var svg = d3.select("#d3-example").select("svg")
if (svg.empty()) {
svg = d3.select("#d3-example").append("svg")
.attr("width", width)
.attr("height", height);
}
d3.json("graph.json", function(error, graph) {
links = graph.links
var nodes = {};
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
});
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.gravity(0.1)
.linkDistance(200)
.on('tick', tick)
.charge(-1000)
.start();
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link').attr('id', function(d){return d.id});
d3.selectAll(".link").each(function(d) {
svg
.append('svg:text').attr("class", "LinkLabelText")
.style("font-size", 15)
.style("fill", "teal")
.attr("x", (this.getTotalLength() / 2))
.attr("y", "-20")
.append('textPath')
.attr("xlink:href", "#" + d.id)
.html(function() {
return d.target.name;
});
});
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d.target.name);
return d.target.name;
});
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.02)
node.append("svg:text")
.text(function(d) { return d.name });
function tick(e) {
node.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
.call(force.drag);
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; });
};
})
When user drag node by mouse click and release mouse click, node is rotating continuously on mouse event as shown in
JS Fiddle Example
This issue is reproduce on IE11 in windows 10.
What is the solution to this issue?
var width = 500;
var height = 500;
var nodeWidth = 40;
var nodeHeight = 40;
var circleRadius = 5;
var diagramLayout;
var graphData = {"nodes":[{"uid":"Term20","name":"Term20","image":"images/Term.png"},{"uid":"glossforArrow","name":"glossforArrow","image":"images/Glossary.png"},{"uid":"Term43","name":"Term43","image":"images/Term.png"},{"uid":"Term1","name":"Term43","image":"images/Term.png"},{"uid":"Term2","name":"Term43","image":"images/Term.png"}],"links":[{"source":"glossforArrow","target":"Term20","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term43","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term1","direction":"output","label":"Owned Terms"},{"source":"glossforArrow","target":"Term2","direction":"output","label":"Owned Terms"}]};
forceInitialize(graphData)
function forceInitialize(graphData){
diagramLayout = d3.select("#diagramLayout")
.attr("id", "diagramLayout")//set id
.attr("width", width)//set width
.attr("height", height)//set height
.append("g")
.attr("transform", "translate(" + 20 + "," + 20 + ")")
markerRefx = 35;
simulation = d3.forceSimulation();
alphaMulti = 1;
simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(70).strength(0))
.force("charge", d3.forceManyBody().distanceMin(20).distanceMax(50))
.force("centre", d3.forceCenter(width/2 , height/2))
.force("x", d3.forceX(2))
.force("y", d3.forceY(10))
.force("collide", d3.forceCollide().radius(function(d) { return 80; }).iterations(2))
simulation.on('end', function() {
simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(30).strength(0.0).iterations(10))
.force("x", d3.forceX().strength(0))
.force("y", d3.forceX().strength(0))
});
force(graphData);
}
//Force Layout
function force(graphData) {
var linkEnter = diagramLayout.selectAll(".links");
linkEnter = linkEnter.data(graphData.links)
.enter().append("g")
.attr("class", "links")
var link = linkEnter.append("line")
.attr("stroke-width", function(d) { return Math.sqrt(2); })
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000" )
var node = diagramLayout.selectAll(".node");
node = node.data(graphData.nodes, function(d) { return d.uid; });
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("height", nodeHeight)
.attr("width", nodeWidth)
.on('contextmenu', menuCall);
var nodeIcon = nodeEnter.append("rect")
.attr("class", "rect")
.attr("x", -20)
.attr("y", -20)
.attr("rx", 10)
.attr("width", 40)
.attr("height", 40)
.attr("stroke-width", function(d) { return Math.sqrt(2); })
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000" )
.attr("fill", "blue" )
nodeIcon.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
simulation
.nodes(graphData.nodes)
.on("tick", ticked);
setTimeout(function tick(){
simulation.tick();
if(simulation.alpha() >= .005);
setTimeout(tick, 0);
}, 0);
simulation.force("link")
.links(graphData.links);
simulation.restart();
function ticked(e) {
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; });
nodeEnter.attr("transform", function(d) {d.fixed = true; return "translate(" + d.x + "," + d.y + ")"; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
d3.select(this).classed("fixed", d.fixed = false);
d3.selectAll(".node").fixed = true;
}
function menuCall(di,i) {
}
}
I have created a d3 force layout, and it works very well. Now I will add a group of data to my graph. I hope I could control the center of my new nodes. For example, supposed the center is (100,100), I hope the new nodes lay out into rectangle area like [(50,50) to (150,150)] as a whole.
var width = 500,
height = 500;
var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];
// init force
var force = d3.layout.force()
.charge(-120)
.linkDistance(120)
.size([width, height]);
// init svg
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// set tick function
force.on("tick", function () {
d3.selectAll(".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;
});
// controll the coordinates here
d3.selectAll(".node").attr("transform", function(d){
if(d.flag == 1){
d.x = Math.max(50, Math.min(150, d.x));
d.y = Math.max(50, Math.min(150, d.y));
}
return "translate("+d.x+","+d.y+")";
});
}).on('end', function(){
svg.selectAll(".node").each(function(d){d.fixed=true;});
});
function setData(ns, ls){
var update = svg.selectAll(".link").data(ls);
update.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
update.exit().remove();
update = svg.selectAll(".node").data(ns);
update.enter().append("g")
.attr("class", "node")
.attr("id", function(d){return d.id})
.call(force.drag)
.call(function(p){
p.append("image")
.attr("class", "nodeimage")
.attr("width", "30px")
.attr("height", "30px")
.attr("x", "-15px")
.attr("y", "-15px");
p.append("text")
.attr("class", "nodetext")
.attr("dx", "-10px")
.attr("dy", "20px")
.style("font-size", "15px")
.text(function(d){return d.n});
});
update.exit().remove();
update.selectAll(".nodeimage")
.each(function() {
d3.select(this).datum(d3.select(this.parentNode).datum());
})
.attr("xlink:href", function(d){
var img;
if(d.flag == 1){
img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
}else{
img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
}
return img;
});
force.nodes(ns)
.links(ls)
.start();
}
//init
setData(nodes, links);
setTimeout(function(){
//generate new data and merge to old data
nodes = nodes.concat(generateNewData());
setData(nodes, links);
//how do i control the coordinate of new nodes?
}, 3000);
function generateNewData(){
var ns = [];
for(var i = 0; i < 10; i++){
ns.push({id:i+100,n:'n'+i,flag:1});
}
return ns;
}
Here is my demo of jsfiddle:http://jsfiddle.net/cs4xhs7s/4/
The latest demo shows that the nodes can display in the rectangle, however, their coordinates are the same. I hope it is an available force layout.
https://jsfiddle.net/wpnq15mf/1/
var width = 500,
height = 500;
var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];
// init force
var force = d3.layout.force()
.charge(-500)
.linkDistance(120)
.gravity(0.1)
.size([width, height]);
// init svg
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// set tick function
force.on("tick", function () {
d3.selectAll(".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;
});
// controll the coordinates here
d3.selectAll(".node").attr("transform", function(d){
if(d.flag == 1){
d.x = Math.max(50, Math.min(150, d.x));
d.y = Math.max(50, Math.min(150, d.y));
}
return "translate("+d.x+","+d.y+")";
});
}).on('end', function(){
svg.selectAll(".node").each(function(d){d.fixed=true;});
});
function setData(ns, ls){
var update = svg.selectAll(".link").data(ls);
update.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
update.exit().remove();
update = svg.selectAll(".node").data(ns);
update.enter().append("g")
.attr("class", "node")
.attr("id", function(d){return d.id})
.call(force.drag)
.call(function(p){
p.append("image")
.attr("class", "nodeimage")
.attr("width", "30px")
.attr("height", "30px")
.attr("x", "-15px")
.attr("y", "-15px");
p.append("text")
.attr("class", "nodetext")
.attr("dx", "-10px")
.attr("dy", "20px")
.style("font-size", "15px")
.text(function(d){return d.n});
});
update.exit().remove();
update.selectAll(".nodeimage")
.each(function() {
d3.select(this).datum(d3.select(this.parentNode).datum());
})
.attr("xlink:href", function(d){
var img;
if(d.flagx == 1){
img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
}else{
img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
}
return img;
});
force.nodes(ns)
.links(ls)
.start();
}
//init
setData(nodes, links);
setTimeout(function(){
//generate new data and merge to old data
nodes = nodes.concat(generateNewData());
links = links.concat(generateNewLinks());
setData(nodes, links);
//how do i control the coordinate of new nodes?
}, 3000);
function generateNewData(){
var ns = [];
ns.push({id:6,n:'n'+i,flag:1, flagx:1});
for(var i = 1; i < 10; i++){
ns.push({id:i+6,n:'n'+i, flagx:1});
}
return ns;
}
function generateNewLinks(){
var ns = [];
ns.push({source:7,target:8});
ns.push({source:7,target:9});
ns.push({source:7,target:10});
ns.push({source:7,target:11});
ns.push({source:7,target:12});
ns.push({source:7,target:13});
ns.push({source:7,target:14});
ns.push({source:7,target:15});
ns.push({source:7,target:16});
return ns;
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Fiddle: http://jsfiddle.net/jzaun/SCb7T/
Code:
var width = 500,
height = 500;
var dotSize = 50;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-10020)
.linkDistance(dotSize)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = {
"nodes":[
{"name":"Name","group":1},
{"name":"Name","group":2},
{"name":"Name","group":3},
{"name":"Name","group":4}
],
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":0,"value":1},
{"source":3,"target":0,"value":1}
]
};
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", dotSize)
.style("fill", function(d) { return color(d.group); });
node.append("image")
.attr("xlink:href", "http://lorempixel.com/64/64/cats")
.attr("x", -32)
.attr("y", -32)
.attr("width", 64)
.attr("height", 64);
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; });
});
You can't append image to circle elements -- the SVG spec doesn't allow this. Instead, append the images directly and set transform on them in the tick function instead of cx/cy.
Complete example here.
I need help adding a second series of data to my bar chart. Currently I'm populating the bars from the glob key in my dataset. This will be the first series. The second series I would like to be is local.
How do I go about adding that?
Play with my JSFiddle here.
var w = 300;
var h = 200;
var colors = ["#377EB8", "#4DAF4A"];
var dataset = [
{"keyword": "payday loans", "glob": 1500000, "local": 673000, "cpc": "14.11"},
{"keyword": "title loans", "glob": 165000, "local": 165000, "cpc": "12.53" },
{"keyword": "personal loans", "glob": 550000, "local": 301000, "cpc": "6.14"}
];
var data = [[1500000, 165000, 550000],
[673000, 165000, 301000]];
var tdata = d3.transpose(dataset.glob, dataset.local);
var series = 2; // Global & Local
var x0Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var x1Scale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {return d.glob;})])
.range([0, h]);
var glob = function(d) {
return d.glob;
};
var cpc = function(d) {
return d.cpc;
};
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w)
.attr("height", h);
// Graph Bars
svg.selectAll("rect")
.data(dataset, cpc, glob)
.enter()
.append("rect")
.attr("x", function(d, i){
return x0Scale(i);
})
.attr("y", function(d) {
return h - yScale(d.glob);
})
.attr("width", x0Scale.rangeBand())
.attr("height", function(d) {
return yScale(d.glob);
})
.attr("fill", colors[1])
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + x0Scale.rangeBand() / 3;
var yPosition = parseFloat(d3.select(this).attr("y")) + yScale;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
//Create labels
svg.selectAll("text")
.data(dataset, glob)
.enter()
.append("text")
.text(function(d) {
return commaFormat(d.glob);
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x0Scale(i) + x0Scale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d.glob) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
It's easiest to create an svg group (<g>) element for each set of data, and then add the individual parts to each group. For example, roughly:
http://jsfiddle.net/WXMwv/1/
var sets = svg.selectAll(".set")
.data(dataset)
.enter().append("g")
.attr("class","set")
.attr("transform",function(d,i){
return "translate(" + x0Scale(i) + ",0)"
})
.on("mouseover", function(d,i) {
//Create x value from data index
var xPosition = parseFloat(x0Scale(i) + x0Scale.rangeBand() / 6);
var yPosition = 0;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#cpcVal")
.text(d.cpc);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
});
sets.append("rect")
.attr("class","glob")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.glob);
})
.attr("height", function(d){
return h - yScale(d.glob);
})
.attr("fill", colors[1])
sets.append("rect")
.attr("class","local")
.attr("width", x0Scale.rangeBand()/2)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("x", x0Scale.rangeBand()/2)
.attr("height", function(d){
return h - yScale(d.local);
})
.attr("fill", colors[0])
The text elements are left as an exercise for the reader :)