Need help using d3.js to create Stacked Bar Chart - javascript

I am currently trying to convert this (mbostock.github.com/d3/ex/stack.html) vertical stacked bar chart using d3.js to a horizontal stacked bar chart, but I have had no luck. if anyone has an example of a horizontally stacked bar chart from d3.js or knows how to modify the following code correctly or point me in the correct direction that would be a great help.
var margin = 20,
width = 960,
height = 500 - .5 - margin,
mx = m,
my = d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
}),
mz = d3.max(data, function(d) {
return d3.max(d, function(d) {
return d.y;
});
}),
x = function(d) { return d.x * width / mx; },
y0 = function(d) { return height - d.y0 * height / my; },
y1 = function(d) { return height - (d.y + d.y0) * height / my; },
y2 = function(d) { return d.y * height / mz; }; // or `my` to not rescale
var vis = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height + margin);
var layers = vis.selectAll("g.layer")
.data(data)
.enter().append("g")
.style("fill", function(d, i) { return color(i / (n - 1)); })
.attr("class", "layer");
var bars = layers.selectAll("g.bar")
.data(function(d) { return d; })
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d) + ",0)"; });
bars.append("rect")
.attr("width", x({x: .9}))
.attr("x", 0)
.attr("y", height)
.attr("height", 0)
.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", y1)
.attr("height", function(d) { return y0(d) - y1(d); });

The trick is: treat it almost the same as a vertical stacked bar chart, but invert the x and y values before stacking, and then back again once stacked. Note the comments in the code below.
My blog post about this: http://datashaman.github.io/2014/01/26/horizontal-stacked-bar-chart-d3/
Demo of the code below: http://bl.ocks.org/datashaman/8621955
jsFiddle: http://jsfiddle.net/datashaman/rBfy5/2/
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
.bar {
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
#tooltip {
position: absolute;
text-align: center;
width: 40px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="tooltip" class="hidden">
<p><span id="value">100</span></p>
</div>
<script>
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
},
legendPanel = {
width: 240
},
width = 700 - margins.left - margins.right - legendPanel.width,
height = 100 - margins.top - margins.bottom,
dataset = [
{
data: [
{ month: 'Aug', count: 123 },
{ month: 'Sep', count: 234 },
{ month: 'Oct', count: 345 }
],
name: 'Series #1'
},
{
data: [
{ month: 'Aug', count: 235 },
{ month: 'Sep', count: 267 },
{ month: 'Oct', count: 573 }
],
name: 'Series #2'
}
],
series = dataset.map(function(d) { return d.name; }),
dataset = dataset.map(function(d) {
return d.data.map(function(o, i) {
// Structure it so that your numeric
// axis (the stacked amount) is y
return {
y: o.count,
x: o.month
};
});
}),
stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function(group) {
return group.map(function(d) {
// Invert the x and y values, and y0 becomes x0
return {
x: d.y,
y: d.x,
x0: d.y0
};
});
}),
svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
xMax = d3.max(dataset, function(group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
}),
xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]),
months = dataset[0].map(function(d) { return d.y; }),
_ = console.log(months),
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1),
xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom'),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('left'),
colours = d3.scale.category10(),
groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function(d, i) {
return colours(i);
}),
rects = groups.selectAll('rect')
.data(function(d) { return d; })
.enter()
.append('rect')
.attr('x', function(d) { return xScale(d.x0); })
.attr('y', function(d, i) { return yScale(d.y); })
.attr('height', function(d) { return yScale.rangeBand(); })
.attr('width', function(d) { return xScale(d.x); })
.on('mouseover', function(d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.text(d.x);
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function() {
d3.select('#tooltip').classed('hidden', true);
})
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
svg.append('rect')
.attr('fill', 'yellow')
.attr('width', 160)
.attr('height', 30 * dataset.length)
.attr('x', width + margins.left)
.attr('y', 0);
series.forEach(function(s, i) {
svg.append('text')
.attr('fill', 'black')
.attr('x', width + margins.left + 8)
.attr('y', i * 24 + 24)
.text(s);
svg.append('rect')
.attr('fill', colours(i))
.attr('width', 60)
.attr('height', 20)
.attr('x', width + margins.left + 90)
.attr('y', i * 24 + 6);
});
</script>
</body>
</html>

Try Frank Guerino's examples:
http://bl.ocks.org/2141479 - horizontal bar chart
and
http://bl.ocks.org/2354192 - Multiple D3 Top Down and Bottom Up Stacked Bar Charts (without d3.layout.stack)
Combine the horizontal code with the Crimea example or Multiple D3 Top Down example and you should be on your track. Mainly what you have to look for is how to calculate the coordinates right in the horizontal setup. The rest is the same like any other stack example.
Best regards!

Related

Using tooltip in D3 chord diagram to get value - Error: "property of target undefined"

I am trying to create a chord diagram and have the value of each ribbons source and target value as seen be accessible when they are hovered over. I also want to be able to access the index of that value to refer to use the colour.
At the moment I am getting an error of d not being defined whenever I hover over - I'm not sure how to access the values I need.. I don't want to add any other .js libraries if possible.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 0.67;
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 14px;
text-align: center;
}
</style>
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.5 - 40,
innerRadius = outerRadius - 30;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc);
var groupTick = group.selectAll(".group-tick")
.data(function(d) { return groupTicks(d, 1e3); })
.enter().append("g")
.attr("class", "group-tick")
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; });
groupTick.append("line")
.attr("x2", 6);
groupTick
.filter(function(d) { return d.value % 5e3 === 0; })
.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return formatValue(d.value); });
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover", function(d) {
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(function(d) {
return (d.target.index) + "Hello";
});
})
.on("mouseout", function(d){
tooltip
.style("display", "none")
})
;
// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, step).map(function(value) {
return {value: value, angle: value * k + d.startAngle};
});
}
</script>
I'm using the tutorial here: https://bl.ocks.org/mbostock/4062006
The issue is that there is no datum associated with your tooltip:
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
So when you say tooltip.html(function(d) { ... there is no datum associated with that element to use.
Instead, try to use the datum associated with the selected chord:
.on("mouseover", function(d) { // the datum you want
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(d.target.index + "Hello");
})
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 0.67;
}
.toolTip {
position: absolute;
display: none;
min-width: 80px;
height: auto;
background: none repeat scroll 0 0 #ffffff;
border: 1px solid #6F257F;
padding: 14px;
text-align: center;
}
</style>
<svg width="960" height="960"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.5 - 40,
innerRadius = outerRadius - 30;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc);
var groupTick = group.selectAll(".group-tick")
.data(function(d) { return groupTicks(d, 1e3); })
.enter().append("g")
.attr("class", "group-tick")
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; });
groupTick.append("line")
.attr("x2", 6);
groupTick
.filter(function(d) { return d.value % 5e3 === 0; })
.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return formatValue(d.value); });
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover", function(d) {
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html(d.target.index + "Hello");
})
.on("mouseout", function(d){
tooltip
.style("display", "none")
})
;
// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, step).map(function(value) {
return {value: value, angle: value * k + d.startAngle};
});
}
</script>

How can i make count to be displayed permanently at the end of graph bars?

I have working fiddle of this code here. I am trying my hands on d3. To display the count right now i am using tooltips. So it displays value on mouseover . How can i append text to the bars, so that the count is displayed permanently ,independent of any mouse event.
Here is the working fiddle
rects = groups.selectAll('rect')
.data(function (d) {
return d;
})
.enter()
.append('rect')
.attr('x', function (d) {
return xScale(d.x0);
})
.attr('y', function (d, i) {
return yScale(d.y);
})
.attr('height', function (d) {
return yScale.rangeBand();
})
.attr('width', function (d) {
return xScale(d.x);
})
.attr('class', function (d) {
if(d.month == 'Open/New'){
return 'hm-statusNew';
}else if(d.month == 'In Progress'){
return 'hm-inProgress';
}else{
return 'hm-completed';
}
})
.on('mouseover', function (d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.text(d.x);
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function () {
d3.select('#tooltip').classed('hidden', true);
})
Try this way :-
groups.selectAll('.bartext')
.data(function(d) {
return d;
})
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "end")
.attr("fill", "white")
.attr('x', function(d) {
return xScale(d.x0) + xScale(d.x);
})
.attr('y', function(d, i) {
return yScale(d.y) + yScale.rangeBand() / 2;
})
.text(function(d) {
return d.x;
});
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
},
legendPanel = {
width: 180
},
width = 600 - margins.left - margins.right - legendPanel.width,
height = 200 - margins.top - margins.bottom,
dataset = [{
data: [{
month: 'Open/New',
count: 123
}, {
month: 'In Progress',
count: 234
}, {
month: 'Completed',
count: 345
}],
name: 'Series #1'
}
],
series = dataset.map(function(d) {
return d.name;
}),
dataset = dataset.map(function(d) {
return d.data.map(function(o, i) {
// Structure it so that your numeric
// axis (the stacked amount) is y
return {
y: o.count,
x: o.month
};
});
}),
stack = d3.layout.stack();
stack(dataset);
var dataset = dataset.map(function(group) {
return group.map(function(d) {
// Invert the x and y values, and y0 becomes x0
return {
x: d.y,
y: d.x,
x0: d.y0
};
});
}),
svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right + legendPanel.width)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
xMax = d3.max(dataset, function(group) {
return d3.max(group, function(d) {
return d.x + d.x0;
});
}),
xScale = d3.scale.linear()
.domain([0, xMax])
.range([0, width]),
months = dataset[0].map(function(d) {
return d.y;
}),
yScale = d3.scale.ordinal()
.domain(months)
.rangeRoundBands([0, height], .1),
xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom'),
yAxis = d3.svg.axis()
.scale(yScale)
.orient('left'),
colours = d3.scale.category10(),
groups = svg.selectAll('g')
.data(dataset)
.enter()
.append('g')
.style('fill', function(d, i) {
return colours(i);
}),
rects = groups.selectAll('rect')
.data(function(d) {
return d;
})
.enter()
.append('rect')
.attr('x', function(d) {
return xScale(d.x0);
})
.attr('y', function(d, i) {
return yScale(d.y);
})
.attr('height', function(d) {
return yScale.rangeBand();
})
.attr('width', function(d) {
return xScale(d.x);
})
.attr('class', function(d) {
if (d.month == 'Open/New') {
return 'hm-statusNew';
} else if (d.month == 'In Progress') {
return 'hm-inProgress';
} else {
return 'hm-completed';
}
})
.on('mouseover', function(d) {
var xPos = parseFloat(d3.select(this).attr('x')) / 2 + width / 2;
var yPos = parseFloat(d3.select(this).attr('y')) + yScale.rangeBand() / 2;
d3.select('#tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.select('#value')
.text(d.x);
d3.select('#tooltip').classed('hidden', false);
})
.on('mouseout', function() {
d3.select('#tooltip').classed('hidden', true);
});
groups.selectAll('.bartext')
.data(function(d) {
return d;
})
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "end")
.attr("fill", "white")
.attr('x', function(d) {
return xScale(d.x0) + xScale(d.x);
})
.attr('y', function(d, i) {
return yScale(d.y) + yScale.rangeBand() / 2;
})
.text(function(d) {
return d.x;
});
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'axis')
.call(yAxis);
.axis path, .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
#tooltip {
position: absolute;
text-align: center;
width: 40px;
height: auto;
padding: 5px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 14px;
line-height: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="tooltip" class="hidden">
<p><span id="value">100</span>
</p>
</div>

Legend to be aligned after pie chart but getting aligned before

Hello I want to right align my legend after the pie chart. But with my codes they are getting aligned to left before pie chart. What CSS changes should I make to do this.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Testing Pie Chart</title>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<style type="text/css">
/* #container {
margin: 5%;
height: 50%;
width: 50%;
}*/
#chart {
position: absolute;
background-color: #eee;
/* height: 50%;
width: 50%; */
}
#chart legend{
position: absolute;
}
.tooltip {
background: #eee;
box-shadow: 0 0 5px #999999;
color: #900C3F;
display: inline-block;
font-size: 12px;
position: absolute;
text-align: center;
width: 10%;
z-index: 10;
opacity: 1;
}
rect {
stroke-width: 2;
}
path {
stroke: #ffffff;
stroke-width: 0.5;
}
div.tooltip {
position: absolute;
z-index: 999;
padding: 10px;
background: #f4f4f4;
border: 0px;
border-radius: 3px;
pointer-events: none;
font-size: 11px;
color: #080808;
line-height: 16px;
border: 1px solid #d4d4d4;
}
</style>
</head>
<body>
<div id="container">
<svg id="chart" viewBox="0 0 960 500" perserveAspectRatio="xMinYMid">
<div id="toolTip" class="tooltip" style="opacity: 0;"></div>
<script type="text/javascript">
var div = d3.select("#toolTip");
var data = [
["192.168.12.1", 20],
["76.09.45.34", 40],
["34.91.23.76", 80],
["192.168.19.32", 16],
["192.168.10.89", 50],
["192.178.34.07", 18],
["192.168.12.98", 30]];
var data = data.map(function(d) {
return {
IP: d[0],
count: d[1]
};
});
var width = 500,
height = 300;
var radius = Math.min(width, height) / 2 - 50;
var legendRectSize = 18,
legendSpacing = 4;
var color = d3.scale.category20b();
var arc = d3.svg.arc()
.outerRadius(radius);
var arcOver = d3.svg.arc()
.outerRadius(radius + 5);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.count; });
var labelArc = d3.svg.arc()
.outerRadius(radius - 40)
.innerRadius(radius - 40);
var svg = d3.select("#chart").append("svg")
.datum(data)
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var arcs = svg.selectAll(".arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
var arcs2 = svg.selectAll(".arc2")
.data(pie)
.enter().append("g")
.attr("class", "arc2");
arcs.append("path")
.attr("fill", function(d, i) { return color(i); })
.on("mouseover", function(d) {
var htmlMsg="";
div.transition()
.style("opacity",0.9);
var total = d3.sum(data.map(function(d) {
return d.count;
}));
var percent = Math.round(1000 * d.data.count / total) / 10;
div.html(
"IP :"+ d.data.IP +""+"<br/>"+
"Count : " + d.data.count +"<br/>" +
"Percent: " + percent + '%'+ htmlMsg)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY) + "px");
svg.selectAll("path").sort(function (a, b) {
if (a != d) return -1;
else return 1;
});
var endAngle = d.endAngle + 0.1;
var startAngle = d.startAngle - 0.1;
var arcOver = d3.svg.arc()
.outerRadius(radius + 10).endAngle(endAngle).startAngle(startAngle);
d3.select(this)
.attr("stroke","white")
.transition()
.ease("bounce")
.duration(1000)
.attr("d", arcOver)
.attr("stroke-width",6);
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
d3.select(this).transition()
.attr("d", arc)
.attr("stroke","none");
})
.transition()
.ease("bounce")
.duration(2000)
.attrTween("d", tweenPie);
function tweenPie(b) {
b.innerRadius = 0;
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arc(i(t)); };
}
var k=0;
arcs2.append("text")
.transition()
.ease("elastic")
.duration(2000)
.delay(function (d, i) {
return i * 250;
})
.attr("x","6")
.attr("dy", ".35em")
.text(function(d) { if(d.data.count >0){ k = k+1; return d.data.count;} }) //We can define any value for (d.data.count >__ according to our need. Like if we have smaller values less than 10 then no need to dislay them as they can be overlapped on pie slice margins)// If not needed to display make this comment
.attr("transform", function(d) { if (k >1){return "translate(" + labelArc.centroid(d) + ") rotate(" + angle(d) + ")";} else{return "rotate(-360)";} })
.attr("font-size", "10px");
function type(d) {
d.count = +d.count;
return d;
}
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
var legend = d3.select("#chart")
.append("svg")
.attr("class", "legend")
.attr("width", radius+50)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain())
.enter()
.append("g")
.attr("transform", "translate(" + width * 1/4 + "," + height * 1/4 + ")")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.data(data)
.text(function(d,i) { return d.IP; });
</script>
</svg>
</div>
<script type="text/javascript">
var chart = $("#chart"),
aspect = chart.width() / chart.height(),
container = chart.parent();
$(window).on("resize", function() {
var targetWidth = container.width();
var targetHeight = container.height();
chart.attr("width", targetWidth);
chart.attr("height", Math.round(targetWidth / aspect));
}).trigger("resize");
</script>
</script>
</body>
</html>
Please give some idea how to solve this problem. I am stuck in this code.
Thanks for help in advance.
You need to add x and y attributes to the legend.
legend.attr("x", width - 65)
.attr("y", 25)
Here is the full code and working example for you

Add space between x-axis and line with time scale

I am using an example below.
HTML-
<div id="chart-holder"></div>
CSS-
.chart-holder {
position: relative;
font-family: Helvetica, sans-serif;
}
.chart-tip {
display: none;
position: absolute;
min-width: 70px;
top: 0px;
left: 0px;
padding: 3px 5px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: .8em;
text-shadow: none;
text-align: center;
z-index: 10500;
border-radius: 3px;
}
.v {
display: block;
font-size: 1.2em;
}
path {
stroke: #0da9c0;
stroke-width: 2;
fill: none;
}
path.domain {
stroke: #aaa;
stroke-width: 1;
}
line {
stroke: #aaa;
}
text {
font-family: Arial;
font-size: 9pt;
fill: #555;
}
circle {
cursor: pointer;
}
Javascript-
var data = [["2013-01-24 06:38:02.235191", 52], ["2013-01-23 06:38:02.235310", 54], ["2013-01-22 06:38:02.235330", 45], ["2013-01-21 06:38:02.235346", 53]],
maxValue = d3.max(data, function (d) { return d[1]; }),
margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 500 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
svg, x, y, xAxis, yAxis, line;
$.each(data, function(index, val) {
val[0] = new Date(val[0]);
});
x = d3.time.scale()
.range([0, width])
y = d3.scale.linear()
.domain([0, maxValue])
.range([height, 0]);
xAxis = d3.svg.axis()
.scale(x)
.tickSize(4, 2, 0)
.ticks(d3.time.days, 1)
.tickFormat(d3.time.format("%m/%d"))
.orient("bottom");
yAxis = d3.svg.axis()
.scale(y)
// .ticks(5)
// .tickValues([0, maxValue * 0.25, maxValue * 0.5, maxValue * 0.75, maxValue])
.tickSize(4, 2, 0)
.orient("left");
line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
svg = d3.select("#chart-holder").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(d3.extent(data, function(d) { return d[0]; }));
y.domain([d3.min(data, function (d) { return d[1]; })-1, maxValue]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.attr("transform", function(d) {
return "rotate(-60)translate(" + -this.getBBox().height * 1.7 + "," +
-this.getBBox().width/4 + ")";
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 6)
// .attr("dy", ".71em")
// .style("text-anchor", "end")
// .text("Engagement");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("fill", "#0b8da0")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d[0]); })
.attr("cy", function(d) { return y(d[1]); })
.on("mouseover", function(d) { showData(this, d);})
.on("mouseout", function() { hideData(this);});
function showData(obj, d) {
var coord = d3.mouse(obj);
var chartTip = d3.select(".chart-tip");
var format = d3.time.format("%b %d, %Y");
// enlarge circle
d3.select(obj)
.attr('r', 5);
// now we just position the chartTip roughly where our mouse is
chartTip.style("left", (coord[0] + 60) + "px" );
chartTip.style("top", (coord[1] + 10) + "px");
$(".chart-tip").html('<span class="v">' + d[1] + '</span>' + format(d[0]));
$(".chart-tip").fadeIn(100);
}
function hideData(obj) {
// enlarge circle
d3.select(obj)
.attr('r', 3.5);
$(".chart-tip").fadeOut(100);
}
$('#chart-holder').append($('<div />', {
'class': 'chart-tip'
}));
Here is the fiddle
http://jsfiddle.net/murli2308/n7Vmr/12/
I can add space between axis and line if I am using ordinal scale or linear scale. But when I am using time scale, I am not able to add space between axis and line.
I tried
x = d3.time.scale()
.range([0, width]);
It is not working.
The x.domain is the user space side of the user space to pixel space mapping. In your code you are setting the domain as:
x.domain(d3.extent(data, function(d) { return d[0] - 1; }));
Which says start my x axis at my min time minus 1 millisecond.
If you want some space before / after the line. Try:
x.domain([
d3.min(data, function(d) { return d[0].getTime() - 1.8e+7; }),
d3.max(data, function(d) { return d[0].getTime() + 1.8e+7; })
]);
Which says start my x axis 5 hours before my min date and stop it 5 hours after my max date.
Updated fiddle.
EDITS
Just used a percentage based scheme, say you want 5% padding (3.6 hours with your current data):
var minDate = d3.min(data, function(d) { return d[0].getTime(); }),
maxDate = d3.max(data, function(d) { return d[0].getTime(); }),
padding = (maxDate - minDate) * .05;
x.domain([minDate - padding, maxDate + padding]);
Updated fiddle.

d3 - legend for parallel coordinate chart not displayed

I have a parallel coordinate chart. I want to display a legend. From the example from Bostock's website, I wrote something similar. But, I can only see the colours of the lines being shown and not the text.
The text is being returned correctly as well.
What am I performing incorrectly ?
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ddd;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
.legend rect {
fill:white;
stroke:black;
opacity:0.8;}
</style>
<body>
<div style="width:1140px; height:550px;margin-left:50px;margin-top:50px;" id="chartDiv"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.5.0/d3-legend.js"></script>
<script>
var margin = {top: 30, right: 10, bottom: 10, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
var svg = d3.select("#chartDiv").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var color = d3.scale.ordinal()
.domain(['white','other','african-american','hispanic','asian/pacific islander'])
.range(['#ffae19','#4ca64c','#4682B4','#737373', '#ff4c4c']);
d3.csv("SurvivalProbability.csv", function(error, cars) {
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
if(d === "Ethnicity") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else if(d === "Site") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else if(d === "Tcategory") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else if(d === "Nodal_Disease") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else if(d === "Chemotherapy") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else if(d === "Local_Therapy") {
y[d] = d3.scale.ordinal()
.domain(cars.map(function(p) { return p[d]; }))
.rangePoints([height, 0]);
}
else {
y[d] = d3.scale.linear()
.domain(d3.extent(cars, function(p) { return +p[d]; }))
.range([height, 0]);
}
return true;
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
/*foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);*/
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path)
.style("stroke", function(d) {
return color(d.Ethnicity);
})
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return {x: x(d)}; })
.on("dragstart", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) { return position(a) - position(b); });
x.domain(dimensions);
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
})
.on("dragend", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(axis.scale(y[d])); })
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) { return d; });
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
var legendRectSize = 10;
var legendSpacing = 100;
var legend = d3.select('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize;
var x = 925;
var y = (i * height) + 50;
return 'translate(' + x + ',' + y + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
});
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
extents = actives.map(function(p) { return y[p].brush.extent(); });
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
}
</script>
The code is correct to show the legend you have to adjust the margin a little.
//rightmargin is set to 200
var margin = {top: 30, right: 200, bottom: 10, left: 10},
and width is like
width = 1100 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
Placement of the legend text as below
var legendRectSize = 10;
var legendSpacing = 10;
var legend = d3.select('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize;
var x = 900;
var y = (i * height) + 50;
return 'translate(' + x + ',' + y + ')';
});
That all :)
Working code here
Hope this helps!

Categories