D3.js add nested data to parent group - d3.js

I have a 'parent' rectangle which has been drawn around smaller rectangles. Here's a live example
The data looks like this:
var allData = [{x:50, y: 60, height: 40, width: 30,
defects: [
{ x: 53, y: 61, width: 10, height: 10 },
{ x: 55, y: 71, width: 10, height: 10 },
{ x: 60, y: 76, width: 10, height: 10 }]},
{x:150, y: 160, height: 50, width: 40,
defects: [
{ x: 151, y: 165, width: 5, height: 5 },
{ x: 160, y: 169, width: 5, height: 5 },
{ x: 165, y: 170, width: 5, height: 5 }]
}];
As you can see, there are 2 rectangles each with 3 smaller rectangles in it called defects.
I'm trying to visualize it but want to do it in 1 selection so I can transform it easier later on.
Here's what I got so far:
var svg = d3.select('#test svg')
.attr("width", 800)
.attr("height", 500);
var groups = svg.selectAll('.defect-group')
.data(allData)
.enter()
.append('g').attr('class', 'defect-group')
.append('rect')
.attr('class', 'defect-area')
.attr('width', function(d) {return d.width})
.attr('height', function(d) {return d.height})
.attr('x', function(d) {return d.x})
.attr('y', function(d) {return d.y});
var defects = groups.selectAll('.defects')
.data(function(d) {
return d.defects;
})
.enter()
.append('rect')
.attr('class', 'defect')
.attr('width', function(d) {return d.width})
.attr('height', function(d) {return d.height})
.attr('x', function(d) {return d.x})
.attr('y', function(d) {return d.y});
The result of this is:
As you can see, I (accidentally) nested the defects inside the 'rect' elements. Since a rect cannot have child elements I would like to put the defects also in the 'defect-group' group, I've tried but can't seem to figure out how to put those in the parent group.
How to do this?

Just get the g element out and append everything to that.
In your code, change
var groups = svg.selectAll('.defect-group')
.data(allData)
.enter()
.append('g').attr('class', 'defect-group')
.append('rect')
.attr('class', 'defect-area')
.attr('width', function(d) {return d.width})
.attr('height', function(d) {return d.height})
.attr('x', function(d) {return d.x})
.attr('y', function(d) {return d.y});
to
var groups = svg.selectAll('.defect-group')
.data(allData)
.enter()
.append('g').attr('class', 'defect-group')
groups
.append('rect')
.attr('class', 'defect-area')
.attr('width', function (d) { return d.width })
.attr('height', function (d) { return d.height })
.attr('x', function (d) { return d.x })
.attr('y', function (d) { return d.y });

Related

D3 V4 how to force Label of stacked bars to visible for small bars? please reply I'm really struggling for it

I'm new to D3JS and struggling with stacked bar below is my dummy code. I tried adding 10 to data to make it visible and then subtract 10 from label to make the label look correct but this cause problem with yAxis where value of bar is crossing the ticker with less then bar value, for example label was showing 95 but bar crossed 100 ticker coz bar size is 95 + 10 = 105. Help me with this.[In image you can find out small bars label are not visible.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>d3.js learning</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style type="text/css">
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
</style>
</head>
<body>
<div id="ashu" style="width: 700px; height:400px;"></div>
<script>
var data = [{
"health": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"health": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"health": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"health": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"health": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"health": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"health": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"health": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"health": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(6).innerTickSize(-width)
.tickPadding(10);
var svg = d3.select("#ashu").append("svg").attr("width",
"100%").attr("height",
"100%").append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: d[c]
};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) {
return (d.y + d.y0);
});
y.domain([-(maximumY * .02), maximumY]).nice();
var layer = svg.selectAll(".stack").data(dataStackLayout).enter()
.append("g").attr("class", "stack").style("fill",
function(d, i) {
return color(i);
});
layer.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand())
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Total : " + (d.y + d.y0))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
layer.selectAll("text")
.data(function(d) {
return d;
})
.enter()
.append("text")
.text(function(d) {
let r = ((d.y < 10) ? (d.y - 10) : d.y);
return d.y;
})
.attr("x", function(d, i) {
return x(d.x) + 15;
})
.attr("y", function(d) {
return y(d.y0);
})
.attr("dy", "-0.15em")
.attr("text-anchor", "middle")
.style("fill", "black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(y_axis);
var legend = svg.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20) + ")";
});
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return xData[i];
});
</script>
</body>
</html>
the labels are being hidden due the order in which the g elements are creating in the DOM, in that elements created later will overlap elements created earlier. This means that the INSOLVENCIES group labels will be hidden by the orange rects for the SPV/ASSETBACKED.
A solution would be to create new g element for your labels, so they always appear on top of your rects. See the snippet below.
Now, where you have small values, there will always be a challenge about where to position the labels. The snippet places them in the middle of the bar, but you may need to play around with that
var data = [{
"health": "JAN",
"INSOLVENCIES": 1,
"SPV/ASSETBACKED": 67,
"OPERATINGCOMPANIES": 13,
"Bank": 15
}, {
"health": "FEB",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 9,
"OPERATINGCOMPANIES": 20,
"Bank": 5
}, {
"health": "MAR",
"INSOLVENCIES": 40,
"SPV/ASSETBACKED": 22,
"OPERATINGCOMPANIES": 21,
"Bank": 99
}, {
"health": "APR",
"INSOLVENCIES": 60,
"SPV/ASSETBACKED": 1,
"OPERATINGCOMPANIES": 99,
"Bank": 90
}, {
"health": "MAY",
"INSOLVENCIES": 2,
"SPV/ASSETBACKED": 27,
"OPERATINGCOMPANIES": 43,
"Bank": 82
}, {
"health": "JUN",
"INSOLVENCIES": 17,
"SPV/ASSETBACKED": 52,
"OPERATINGCOMPANIES": 79,
"Bank": 9
}, {
"health": "JUL",
"INSOLVENCIES": 37,
"SPV/ASSETBACKED": 24,
"OPERATINGCOMPANIES": 35,
"Bank": 51
}, {
"health": "AUG",
"INSOLVENCIES": 16,
"SPV/ASSETBACKED": 17,
"OPERATINGCOMPANIES": 53,
"Bank": 38
}, {
"health": "SEP",
"INSOLVENCIES": 15,
"SPV/ASSETBACKED": 32,
"OPERATINGCOMPANIES": 5,
"Bank": 31
}];
let xData = d3.keys(data[0]);
const yData = xData.shift();
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var margin = {
top: 20,
right: 50,
bottom: 30,
left: 50
},
width = 400,
height = 300,
padding = 100;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var y_axis = d3.svg.axis().scale(y).orient("left").ticks(6).innerTickSize(-width)
.tickPadding(10);
var svg = d3.select("#ashu").append("svg").attr("width",
"100%").attr("height",
"100%").append("g").attr(
"transform",
"translate(" + margin.left + "," + margin.top + ")");
var dataIntermediate = xData.map(function(c) {
return data.map(function(d, yData) {
return {
x: d[Object.keys(d)[0]],
y: d[c]
};
});
});
var dataStackLayout = d3.layout.stack()(dataIntermediate);
x.domain(dataStackLayout[0].map(function(d) {
return d.x;
}));
var total = 0;
var maximumY = d3.max(dataStackLayout[dataStackLayout.length - 1], function(d) {
return (d.y + d.y0);
});
y.domain([0, maximumY]).nice();
var layer1 = svg.selectAll(".stack1").data(dataStackLayout).enter()
.append("g").attr("class", "stack").style("fill",
function(d, i) {
return color(i);
});
var layer2 = svg.selectAll(".stack2").data(dataStackLayout).enter()
.append("g")
.attr("class", "stack")
.style("fill", "black");
layer1.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return y(d.y + d.y0);
})
.attr("height", function(d) {
return y(d.y0) - y(d.y + d.y0);
})
.attr("width", x.rangeBand())
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html("Total : " + (d.y + d.y0))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
layer2.selectAll("text")
.data(function(d) {
return d;
})
.enter()
.append("text")
.text(function(d) {
return d.y;
})
.attr("x", function(d, i) {
return x(d.x) + (x.rangeBand())/2;
})
.attr("y", function(d) {
return y(d.y0 + (d.y/2));
})
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.style("fill", "black");
svg.append("g").attr("class", "axis").attr("transform",
"translate(0," + height + ")").call(xAxis);
svg.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(y_axis);
var legend = svg.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + Math.abs((i - 8) * 20) + ")";
});
legend.append("rect")
.attr("x", width + 10)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width + 32)
.attr("y", 10)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d, i) {
return xData[i];
});
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="ashu" style="width: 700px; height:400px;"></div>

d3.js v4 stacked bar tooltip / hover (using modified d3-tip) offset issue

i am having some trouble with a d3.js v4 stacked bar chart trying to create a hover effect
the goal is to have a the entire stack highlight and a tooltip appear on hover. i think i am looking for away to get the location coordinates and size for the rect to draw over the entirety of the stack
i'm using this modified d3-tip library for v4 for tooltips
https://github.com/VACLab/d3-tip
A picture of the issue speaks a thousand words:
http://nicholasmahoney.com/gj/ex.jpg
--here, i can get pretty close to the x position and i can get the width by using x(d.fellow) and x.bandwith but can't figure out the y component. i'd like the black bars in this example to be the same place and size as the stacked bars
EDIt: adding jsfiddle with code and data,
here is the exact code in action:
http://jsfiddle.net/nickmahoney/ddjbumrx/6/
<svg width="500" height="500">
</svg>
<script>
var svg = d3.select("svg"),
margin = {
top: 40,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var data = [ { "fellow": "demo", "primary": 1, "assistant": 1, "observer": 0, "instructor": 0 }, { "fellow": "alpha", "primary": 22, "assistant": 8, "observer": 0, "instructor": 0 }, { "fellow": "betta", "primary": 0, "assistant": 4, "observer": 0, "instructor": 0 }, { "fellow": "gamma", "primary": 4, "assistant": 2, "observer": 0, "instructor": 0 }, { "fellow": "donkey", "primary": 44, "assistant": 149, "observer": 20, "instructor": 0 },{ "fellow": "donkey", "primary": 44, "assistant": 149, "observer": 20, "instructor": 0 } , { "fellow": "eeee", "primary": 22, "assistant": 8, "observer": 0, "instructor": 0 }, { "fellow": "ffff", "primary": 0, "assistant": 4, "observer": 0, "instructor": 0 }, { "fellow": "gaggggmma", "primary": 4, "assistant": 2, "observer": 0, "instructor": 0 }, { "fellow": "aaaa", "primary": 44, "assistant": 149, "observer": 20, "instructor": 0 },{ "fellow": "ddddddefef", "primary": 44, "assistant": 149, "observer": 20, "instructor": 0 }];
// fix pre-processing
var keys = [];
for (key in data[0]){
if (key != "fellow")
keys.push(key);}
data.forEach(function(d){
d.total = 0;
keys.forEach(function(k){
d.total += d[k];
})
});
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Name:</strong> <span style='color:red'>" + d.fellow + "<br><strong>Primary:</strong>" + d.primary + "</span>";
})
svg.call(tip);
//data.sort(function(a, b) { return b.total - a.total; });
x.domain(data.map(function(d) { return d.fellow; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.data.fellow); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
;
//tooltip bars
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return 40+ x(d.fellow) ; })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.primary) ; })
.attr("height", function(d) { return y(d.primary) ; })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
;
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {return d;});
</script>
Here's a fork of your fiddle: http://jsfiddle.net/s54tbyxb/
You're already computing the total value based on the desired keys. I'm just using that value to determine the y component and the height.
Relevant changes in the code:
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("x", function(d) { return x(d.fellow) ; })
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.total) ;
})
.attr("height", function(d) { return height-y(d.total) ; })
Hope this helps.

Stacked bar chart, color bars

I have a codepen here - https://codepen.io/anon/pen/xpBqxG?editors=1010
I'm doing some simple changes to my starter data and then creating a stacked bar chart.
I'm currently coloring each bar the same color but there should be two colors, one for each key in the stacked chart. Each bar should be spilt into two colors.
How do I color each section of the bar the same color.
.style('fill', (d, i) => {
return colors[i];
});
Apply the style to the groups, not to the rectangles:
let layers = layersArea.selectAll('.layer')
.data(stackedSeries)
.enter()
.append('g')
.attr('class', 'layer')
.style('fill', (d, i) => {
return colors[i];
});
Here is your code with that change only:
let keys = [];
let dataToStack = [];
let totalData = [];
let legendKeys = ['usedInf', 'newInf'];
let w = 800;
let h = 450;
let margin = {
top: 30,
bottom: 40,
left: 50,
right: 20,
};
let width = w - margin.left - margin.right;
let height = h - margin.top - margin.bottom;
let colors = ['#FF9A00', '#FFEBB6', '#FFC400', '#B4EDA0', '#FF4436'];
let data = [{
"one": 10,
"two": 12,
"three": 18,
"four": 22,
"five": 30,
"six": 44,
"seven": 25,
"date": "2015-05-31T00:00:00"
}, {
"one": 30,
"two": 42,
"three": 38,
"four": 62,
"five": 90,
"six": 144,
"seven": 95,
"date": "2015-06-30T00:00:00"
}, {
"one": 30,
"two": 92,
"three": 18,
"four": 100,
"five": 120,
"six": 10,
"seven": 110,
"date": "2015-07-31T00:00:00"
}, ];
for (let i = 0; i < data.length; i++) {
dataToStack.push({
date: data[i]['date'].toString(),
usedInf: data[i]['one'] + data[i]['two'] + data[i]['three'],
newInf: data[i]['four'] + data[i]['five'] + data[i]['six']
});
totalData.push({
date: data[i]['date'].toString(),
total: data[i]['seven']
});
}
let stack = d3.stack()
.keys(legendKeys);
let stackedSeries = stack(dataToStack);
let x = d3.scaleBand()
.domain(dataToStack.map(function(d) {
return d.date;
}))
.rangeRound([0, width])
.padding(0.05);
let y = d3.scaleLinear()
.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, (d) => {
return d[1];
})
})])
.range([height, 0]);
let svg = d3.select('.chart').append('svg')
.attr('class', 'chart')
.attr('width', w)
.attr('height', h);
let chart = svg.append('g')
.classed('graph', true)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
let layersArea = chart.append('g')
.attr('class', 'layers');
let layers = layersArea.selectAll('.layer').data(stackedSeries)
.enter()
.append('g')
.attr('class', 'layer')
.style('fill', (d, i) => {
return colors[i];
});
layers.selectAll('rect')
.data((d) => {
return d
})
.enter()
.append('rect')
.attr('height', (d, i) => {
return y(d[0]) - y(d[1]);
})
.attr('y', (d) => {
return y(d[1]);
})
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth());
chart.append('g')
.classed('x axis', true)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
chart.append('g')
.classed('y axis', true)
.call(d3.axisLeft(y)
.ticks(10));
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="chart"></div>

Nodes drawn randomly in Force Layout graph

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Force Layout Example 1</title>
<style>
.node {
fill: #ccc;
stroke: #fff;
stroke-width: 2px;
}
.link {
stroke: #777;
stroke-width: 2px;
}
.line {
stroke: #777;
stroke-width: 2px;
}
</style>
</head>
<body>
<script src='http://d3js.org/d3.v3.min.js'></script>
<script>
var width = 640,
height = 480;
var nodes = [
{ "x": 200, "y": 200 },
{ "x": 500, "y": 300 },
{ "x": 500, "y": 100 },
//{ "x": 650, "y": 100 },
];
//var nodes = [
// { "x": 200, "y": 200 },
// { "x": 500, "y": 300 },
//{ "x": 500, "y": 100 },
//];
//var links = [
// { source: 0, target: 1 },
// { source: 1, target: 2 },
//];
var links = [
{ source: 0, target: 1 },
{ source: 0, target: 2 },
//{ source: 1, target: 3 },
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(75);
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = svg.selectAll('.node')
.data(nodes)
.enter().append('rect')
.attr('class', 'node');
var subnode = svg.selectAll('.subnode')
.data(nodes)
.enter().append('circle')
.attr('class', 'subnode');
var subnode2 = svg.selectAll('.subnode2')
.data(nodes)
.enter().append('circle')
.attr('class', 'subnode2');
force.on('end', function() {
node.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", 50)
.attr("height", 20);
subnode.attr('r', width/250)
.attr('cx', function(d) { return (d.x); })
.attr('cy', function(d) { return d.y + 10; });
subnode2.attr('r', width/250)
.attr('cx', function(d) { return d.x+50; })
.attr('cy', function(d) { return d.y + 10; });
link.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y+ 10; })
.attr('x2', function(d) { return d.target.x+50; })
.attr('y2', function(d) { return d.target.y+ 10; });
});
force.start();
var line;
function mousedown() {
var m = d3.mouse(this);
//alert(m[0]+"---"+m[1]);
line = svg.append("line")
.attr('x1', m[0])
.attr('y1', m[1])
.attr('x2', m[0])
.attr('y2', m[1]);
svg.on("mousemove", mousemove);
}
function mousemove() {
var m = d3.mouse(this);
line.attr('x2', m[0])
.attr('y2', m[1]);
}
function mouseup() {
svg.on("mousemove", null);
}
</script>
</body>
</html>
The above solution gives below result:
The problem is I dont understand why the graph is drawn reverse and moreover in above code I have commented out some nodes and links if you uncomment them then there is more chaos the whole nodes are drawn in complete random order i.e. increasing more nodes and links create more chaos.
See the JSBIN : http://jsbin.com/yuyolof/edit?html
Take a look at this jsbin http://jsbin.com/himutohimu/1/edit?html,css,output
(I've added a bit too much info in this just to have a better look at what's going on)
You have two subnodes that have the same node data. You are positioning them on "end" like this:
subnode.attr('r', width/250) // black nodes
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y + 10; });
subnode2.attr('r', width/250) // red nodes
.attr('cx', function(d) { return d.x + 50; })
.attr('cy', function(d) { return d.y + 10; });
I've colored the nodes differently in order to better see how this works.
In order for your lines to connect to one kind of subnodes you need to either follow the x and y of the black nodes or the x and y of the red nodes:
// x1 and y1 are the starting point of the line, so in order to follow the
// red nodes, we need to move accordingly with +50 for x and +10 for y.
// the same goes for x2, y2 which are the coordinates for the end of the line
link.attr('x1', function(d) { return d.source.x + 50; })
.attr('y1', function(d) { return d.source.y + 10; })
.attr('x2', function(d) { return d.target.x + 50; })
.attr('y2', function(d) { return d.target.y + 10; });
//Or if you need your lines to follow the black nodes/ dots then x1, y1
// and x2,y2 need to move accordingly to your subnode's x and y,
// so x as it is and y plus 10
// it is one or the other
link.attr('x1', function(d) { return d.source.x; })
.attr('y1', function(d) { return d.source.y + 10; })
.attr('x2', function(d) { return d.target.x; })
.attr('y2', function(d) { return d.target.y + 10; });
So, it is a matter of what nodes (dots) you want to connect and then move the line according to their respective x and y.
Hope this helps!
Good luck!
Edit: How this works with more nodes: http://jsbin.com/nodaruwere/1/edit?html,output

Visualize multidimensional Array in SVG

I have an array of objects which I bind to a g-element. For each g-element I generate a rect-element. It works so far but now I want to have a dynamic number of "parts" in each g-element which should generate a new rect and text element inside the existing rect.
Here you will find my example or on fiddle.net/tnnomejg/
var config = {
width: 600
};
var data = [
{
height: 150,
y: 0,
parts: [
{
title: "Hello",
x: 50,
y: 60,
},
{
title: "World",
x: 350,
y: 60,
}
],
},
{
height: 150,
y: 155,
parts: [
{
title: "Demo",
x: 280,
y: 215,
}
],
},
];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) { return d.y; })
.attr("width", config.width)
.attr("height", function(d) { return d.height; });
Thx.
You could use a subselection binding your parts' data to the corresponding elements using an accessor function like this:
var parts = g.selectAll("g.part")
.data(function(d) { return d.parts; })
.enter()
.append("g")
.attr("class", "part");
Having this subselection at your hands you may insert/append content accessing the corresponding data bound to each group/part.
var config = {
width: 600
};
var data = [
{
height: 150,
y: 0,
parts: [
{
title: "Hello",
x: 50,
y: 60
},
{
title: "World",
x: 350,
y: 60
}
]
},
{
height: 150,
y: 155,
parts: [
{
title: "Demo",
x: 280,
y: 215
}
]
}
];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) { return d.y; })
.attr("width", config.width)
.attr("height", function(d) { return d.height; });
var parts = g.selectAll("g.part")
.data(function(d) { return d.parts; })
.enter()
.append("g")
.attr("class", "part");
parts.append("rect")
.attr({
"class": "part",
"x": function(d) { return d.x; },
"y": function(d) { return d.y; },
"width": 200,
"height":80
});
parts.append("text")
.attr({
"class": "part",
"x": function(d) { return d.x; },
"y": function(d) { return d.y; }
})
.text(function(d) { return d.title; });
g.part rect {
fill:white;
}
g.part text {
fill: red;
stroke: none;
font-size:20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Use d3 selector each method.
g.each(function(d, i) {
var rect = d3.select(this);
d.parts.forEach(function(p) {
rect.append("rect")
.style("fill", "aliceblue")
.attr("x", p.x)
.attr("y", p.y)
.attr("width", config.width / 3)
.attr("height", d.height / 2);
rect.append("text")
.style("stroke", "brown")
.attr("x", p.x)
.attr("y", p.y)
.text(p.title);
});
});
var config = {
width: 600
};
var data = [{
height: 150,
y: 0,
parts: [{
title: "Hello",
x: 50,
y: 60,
}, {
title: "World",
x: 350,
y: 60,
}],
}, {
height: 150,
y: 155,
parts: [{
title: "Demo",
x: 280,
y: 215,
}],
}, ];
var svg = d3.select("body").append("svg").attr("width", config.width).attr("height", 500);
var g = svg.selectAll("g")
.data(data)
.enter()
.append("g");
g.append("rect")
.attr("x", 1)
.attr("y", function(d) {
return d.y;
})
.attr("width", config.width)
.attr("height", function(d) {
return d.height;
});
g.each(function(d, i) {
var rect = d3.select(this);
d.parts.forEach(function(p) {
rect.append("rect")
.style("fill", "aliceblue")
.attr("x", p.x)
.attr("y", p.y)
.attr("width", config.width / 3)
.attr("height", d.height / 2);
rect.append("text")
.style("stroke", "brown")
.attr("x", p.x)
.attr("y", p.y)
.text(p.title);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Resources