Aligning a stacked barchart to the bottom of the chart - d3.js

Thanks in advance for any help.
I'm new to D3 and javascript as a whole. I've been pretty stuck on this for a while now even searching through other similar posts.
I'd like to flip my stacked bar chart appropriately so that it aligns to the bottom of the SVG.
When I do try it the way I think it should be done, I get a "invalid negative value for '' message.
var dataset = [
[
{ x: 0, y: 5 },
{ x: 1, y: 4 },
{ x: 2, y: 2 },
{ x: 3, y: 7 },
{ x: 4, y: 23 }
],
[
{ x: 0, y: 10 },
{ x: 1, y: 12 },
{ x: 2, y: 19 },
{ x: 3, y: 23 },
{ x: 4, y: 17 }
],
[
{ x: 0, y: 22 },
{ x: 1, y: 28 },
{ x: 2, y: 32 },
{ x: 3, y: 35 },
{ x: 4, y: 43 }
]
];
//Width and Height
var w = 500;
h = 300;
//Create SVG canvas
var svg = d3.select('body').append('svg')
.attr('width', w)
.attr('height', h);
//Set up Stack
var stack = d3.layout.stack();
//Stack dataset
stack(dataset);
//Create scales
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset[0].length))
.rangeRoundBands([0,w], 0.05);
var yScale = d3.scale.linear()
.domain([0,
d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})
])
.range([0, h]);
//Create colors for scale
var colors = d3.scale.category10();
//Create a Group for each row of data
var groups = svg.selectAll('g')
.data(dataset)
.enter() //only creates placeholder
.append('g') //creates group
.style('fill', function(d, i) {
return colors(i);
});
//Add a rectangle for each datavalue
var rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) { return yScale(d.y0 + d.y); })
.attr("height", function(d) { return yScale(d.y0) - yScale(d.y0 + d.y); })
.attr("width", xScale.rangeBand());

I think you need to change the way height is getting calculated for each bar. See this plnkr. Is this something you were looking for. May be you can use height (h) variable for your calculations. I changed y parameters like below.
//Add a rectangle for each datavalue
var rects = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
/* .attr("y", function(d) { return yScale(d.y0 + d.y); })
.attr("height", function(d) { return yScale(d.y0) - yScale(d.y0 + d.y); })
.attr("y", function(d) { return yScale(d.y0 ); })
.attr("height", function(d) { return yScale(d.y0 + d.y);})
*/
.attr("y", function(d) {
return yScale(d.y0 + d.y);
})
.attr("height", function(d) {
return h - yScale(d.y0 + d.y);
})
.attr("width", xScale.rangeBand());

Related

Add moving circles to transitioning nested paths in d3.js

I want to add circles to an svg for each path created by the nest so that the circles lead the drawing of the paths. Similar to this fiddle, but with nested data.
Here is some reproducible example code in d3.v4 and a runnable snippet of the paths I would like to add circles to:
var data=[{group:1,x:6,y:8},
{group:1,x:4,y:4},
{group:1,x:1,y:2},
{group:2,x:8,y:3},
{group:2,x:1,y:6},
{group:2,x:7,y:5},
{group:3,x:7,y:1},
{group:3,x:6,y:6},
{group:3,x:3,y:2}];
var height = 600
var width = 800
var svg = d3.select("body")
.append("svg")
.attr("height", "100%")
.attr("width", "100%");
var colours = ["#0000FF",
"#FF0000",
"#00FF00"
];
var line = d3.line()
.x(function(d, i) {
return d.x * 20;
})
.y(function(d, i) {
return d.y * 20;
})
.curve(d3.curveNatural);
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
function transition(selection) {
selection.each(function() {
d3.select(this).transition()
.duration(5000)
.attrTween("stroke-dasharray", tweenDash)
.ease(d3.easeLinear);
})
}
var dataGroup = d3.nest()
.key(function(d) {
return d.group;
})
.entries(data);
dataGroup.forEach(function(d, i) {
var path = svg.append("path")
.attr("d", line(d.values))
.attr("stroke", colours[i])
.attr("stroke-width", 1)
.attr("fill", "none");
transition(d3.selectAll("path"))
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Using this example, I've added circles to your paths. Note that they do not follow the path perfectly, but this is covered by increasing their radius.
A couple of times, you use .forEach() or .each() to iterate over the data or the selection, but you don't need to. d3 is made to work with arrays of data at once, so you can easily apply one transformation to multiple elements, each using their own data. Getting used to that can drastically improve your developer experience.
var data = [{
group: 1,
x: 6,
y: 8
},
{
group: 1,
x: 4,
y: 4
},
{
group: 1,
x: 1,
y: 2
},
{
group: 2,
x: 8,
y: 3
},
{
group: 2,
x: 1,
y: 6
},
{
group: 2,
x: 7,
y: 5
},
{
group: 3,
x: 7,
y: 1
},
{
group: 3,
x: 6,
y: 6
},
{
group: 3,
x: 3,
y: 2
}
];
var height = 600
var width = 800
var svg = d3.select("body")
.append("svg")
.attr("height", "100%")
.attr("width", "100%");
var colours = ["#0000FF",
"#FF0000",
"#00FF00"
];
var line = d3.line()
.x(function(d, i) {
return d.x * 20;
})
.y(function(d, i) {
return d.y * 20;
})
.curve(d3.curveNatural);
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
function tweenCircle(i, paths) {
var path = paths
.filter(function(_, j) { return i === j; })
.node();
var l = path.getTotalLength();
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + [p.x, p.y] + ")";
};
}
function transition(path, circle) {
path.transition()
.duration(2000)
.attrTween("stroke-dasharray", tweenDash)
.ease(d3.easeLinear);
circle.transition()
.duration(2000)
.attrTween("transform", function(d, i) { return tweenCircle(i, path); })
.ease(d3.easeLinear);
}
var dataGroup = d3.nest()
.key(function(d) {
return d.group;
})
.entries(data);
var path = svg.selectAll('path')
.data(dataGroup)
.enter()
.append("path")
.attr("d", function(d) { return line(d.values); })
.attr("stroke", function(d, i) { return colours[i]; })
.attr("stroke-width", 1)
.attr("fill", "none");
var circle = svg.selectAll('circle')
.data(dataGroup)
.enter()
.append("circle")
.attr("fill", function(d, i) { return colours[i]; })
.attr("transform", function(d) {
const start = d.values[0];
return "translate(" + [start.x, start.y] + ")"; })
.attr("r", 8);
transition(path, circle);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

D3js horizontal bar chart: how to add numbers from data at the end of every bar?

I have this code, which draw a bar chart. I'm trying to put the corresponding value inside the bars but in the very end of each bar. I haven't been able to accomplish it. Can you please help me with this? Any suggestion will be highly appreciated.
I'll leave a running snippet so that you can see it and tell where to start to place the values at the end of the bars but inside it.
$(window).on('resize', function (event) {
$("#chart").width(window.innerWidth * 0.9);
$("#chart").height(window.innerHeight);
});
function horizontalGroupBarChart(config) {
function setReSizeEvent(data) {
var resizeTimer;
var interval = 500;
window.removeEventListener('resize', function () {
});
window.addEventListener('resize', function (event) {
if (resizeTimer !== false) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(function () {
$(data.mainDiv).empty();
drawHorizontalGroupBarChartChart(data);
clearTimeout(resizeTimer);
}, interval);
});
}
drawHorizontalGroupBarChartChart(config);
setReSizeEvent(config);
}
function createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange) {
var z = d3.scaleOrdinal()
.range(colorRange);
var mainDivName = mainDiv.substr(1, mainDiv.length);
$(mainDiv).before("<div id='Legend_" + mainDivName + "' class='pmd-card-body' style='margin-top:0; margin-bottom:0;text-align:center'></div>");
var keys = Object.keys(columnsInfo);
keys.forEach(function (d) {
var cloloCode = z(d);
$("#Legend_" + mainDivName).append("<span class='team-graph team1' style='display: inline-block; margin-right:10px;'>\
<span style='background:" + cloloCode + ";width: 10px;height: 10px;display: inline-block;vertical-align: middle;'> </span>\
<span style='padding-top: 0;font-family:Source Sans Pro, sans-serif;font-size: 13px;display: inline;'>" + columnsInfo[d] + " </span>\
</span>");
});
}
function drawHorizontalGroupBarChartChart(config) {
var data = config.data;
var columnsInfo = config.columnsInfo;
var xAxis = config.xAxis;
var yAxis = config.yAxis;
var colorRange = config.colorRange;
var mainDiv = config.mainDiv;
var mainDivName = mainDiv.substr(1, mainDiv.length);
var label = config.label;
var requireLegend = config.requireLegend;
d3.select(mainDiv).append("svg").attr("width", $(mainDiv).width()).attr("height", $(mainDiv).height() * 0.80).attr("class","mainSVG")
var svg = d3.select(mainDiv + " svg"),
margin = { top: 20, right: 20, bottom: 40, left: 40 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var g = svg.append("g").attr("transform", "translate(" +( margin.left*2.3) + "," + margin.top + ")");
if (requireLegend != null && requireLegend != undefined && requireLegend != false) {
$("#Legend_" + mainDivName).remove();
createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange);
}
$(".mainSVG").attr("transform","translate(5,10)")
var y0 = d3.scaleBand()
.rangeRound([height, 0])
.paddingInner(0.1);
var y1 = d3.scaleBand()
.padding(0.05);
var x = d3.scaleLinear()
.rangeRound([0, width - margin.left ]);
var z = d3.scaleOrdinal()
.range(colorRange);
var keys = Object.keys(columnsInfo);
y0.domain(data.map(function (d) {
return d[yAxis];
}));
y1.domain(keys).rangeRound([0, y0.bandwidth()]);
x.domain([0, d3.max(data, function (d) {
return d3.max(keys, function (key) {
return d[key];
});
})]).nice();
var maxTicks = d3.max(data, function (d) {
return d3.max(keys, function (key) {
return d[key];
});
});
var element = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function (d) {
return "translate(0," + y0(d[yAxis]) + ")";
});
var rect = element.selectAll("rect")
.data(function (d, i) {
return keys.map(function (key) {
return { key: key, value: d[key], index: key + "_" + i + "_" + d[yAxis] };
});
})
.enter().append("rect")
.attr("y", function (d) {
return y1(d.key);
})
.attr("width", function (d) {
return x(d.value);
})
.attr("data-index", function (d, i) {
return d.index;
})
.attr("height", y1.bandwidth())
.attr("fill", function (d) {
return z(d.key);
})
var datax = [0,1,2,3,4,5,6,7,8,9,10,11,12];
var tScale= d3.scaleLinear()
.rangeRound([0, width - margin.left ]);
tScale.domain(d3.extent(datax)).nice();
//CBT:add tooltips
var self = {};
self.svg = svg;
self.cssPrefix = "horgroupBar0_";
self.data = data;
self.keys = keys;
self.height = height;
self.width = width;
self.label = label;
self.yAxis = yAxis;
self.xAxis = xAxis;
horBarTooltip.addTooltips(self);
rect.on("mouseover", function () {
var currentEl = d3.select(this);
var index = currentEl.attr("data-index");
horBarTooltip.showTooltip(self, index);
});
rect.on("mouseout", function () {
var currentEl = d3.select(this);
var index = currentEl.attr("data-index");
horBarTooltip.hideTooltip(self, index);
});
rect.on("mousemove", function () {
horBarTooltip.moveTooltip(self);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(maxTicks))
.append("text")
.attr("x", width / 2)
.attr("y", margin.bottom * 0.7)
.attr("dx", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y0).ticks(null, "s"))
.append("text")
.attr("x", height * 0.4 * -1)
.attr("y", margin.left * 0.8 * -1)//y(y.ticks().pop()) + 0.5)
.attr("dy", "0.71em")
.attr("fill", "#00338D")
.attr("font-weight", "bold")
// .attr("text-anchor", "start")
}
var helpers = {
getDimensions: function (id) {
var el = document.getElementById(id);
var w = 0, h = 0;
if (el) {
var dimensions = el.getBBox();
w = dimensions.width;
h = dimensions.height;
} else {
console.log("error: getDimensions() " + id + " not found.");
}
return { w: w, h: h };
}
}
var horBarTooltip = {
addTooltips: function (pie) {
var keys = pie.keys;
// group the label groups (label, percentage, value) into a single element for simpler positioning
var element = pie.svg.append("g")
.selectAll("g")
.data(pie.data)
.enter().append("g")
.attr("class", function (d, i) {
return pie.cssPrefix + "tooltips" + "_" + i
});
tooltips = element.selectAll("g")
.data(function (d, i) {
return keys.map(function (key) {
return { key: key, value: d[key], index: key + "_" + i + "_" + d[pie.yAxis] };
});
})
.enter()
.append("g")
.attr("class", pie.cssPrefix + "tooltip")
.attr("id", function (d, i) {
return pie.cssPrefix + "tooltip" + d.index;
})
.style("opacity", 0)
.append("rect")
.attr("rx", 2)
.attr("ry", 2)
.attr("x", -2)
.attr("opacity", 0.71)
.style("fill", "#000000");
element.selectAll("g")
.data(function (d, i) {
return keys.map(function (key) {
return { key: key, value: d[key], index: key + "_" + i + "_" + d[pie.yAxis] };
});
})
.append("text")
.attr("fill", function (d) {
return "#efefef"
})
.style("font-size", function (d) {
return 10;
})
.style("font-family", function (d) {
return "arial";
})
.text(function (d, i) {
var caption = "" + pie.label.xAxis + ":{value}";
return horBarTooltip.replacePlaceholders(pie, caption, i, {
value: d.value,
});
});
element.selectAll("g rect")
.attr("width", function (d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return dims.w + (2 * 4);
})
.attr("height", function (d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return dims.h + (2 * 4);
})
.attr("y", function (d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return -(dims.h / 2) + 1;
});
},
showTooltip: function (pie, index) {
var fadeInSpeed = 250;
if (horBarTooltip.currentTooltip === index) {
fadeInSpeed = 1;
}
horBarTooltip.currentTooltip = index;
d3.select("#" + pie.cssPrefix + "tooltip" + index)
.transition()
.duration(fadeInSpeed)
.style("opacity", function () {
return 1;
});
horBarTooltip.moveTooltip(pie);
},
moveTooltip: function (pie) {
d3.selectAll("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
.attr("transform", function (d) {
var mouseCoords = d3.mouse(this.parentNode);
var x = mouseCoords[0] + 4 + 2;
var y = mouseCoords[1] - (2 * 4) - 2;
return "translate(" + x + "," + y + ")";
});
},
hideTooltip: function (pie, index) {
d3.select("#" + pie.cssPrefix + "tooltip" + index)
.style("opacity", function () {
return 0;
});
// move the tooltip offscreen. This ensures that when the user next mouseovers the segment the hidden
// element won't interfere
d3.select("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
.attr("transform", function (d, i) {
// klutzy, but it accounts for tooltip padding which could push it onscreen
var x = pie.width + 1000;
var y = pie.height + 1000;
return "translate(" + x + "," + y + ")";
});
},
replacePlaceholders: function (pie, str, index, replacements) {
var replacer = function () {
return function (match) {
var placeholder = arguments[1];
if (replacements.hasOwnProperty(placeholder)) {
return replacements[arguments[1]];
} else {
return arguments[0];
}
};
};
return str.replace(/\{(\w+)\}/g, replacer(replacements));
}
};
var groupChartData = [{ "num": 1, "over": "Singapore" }, { "num": 1.3, "over": "The Netherlands" }, { "num": 2, "over": "United Kingdom" }, { "num": 2.4, "over": "United States"}, { "num": 2.6, "over": "New Zealand" }, { "num": 2.8, "over": "Sweden" }, { "num": 3, "over": "Canada"}, { "num": 3, "over": "UAE" }, { "num": 4, "over": "Australia" }, { "num": 4.4, "over": "France" },{ "num": 5, "over": "South Korea" },{ "num": 5.2, "over": "Germany" },{ "num": 5.5, "over": "Austria" },{ "num": 6, "over": "Austria" },{ "num": 7, "over": "Brazil" },{ "num": 7, "over": "China" },{ "num": 8, "over": "Japan" },{ "num": 10, "over": "Russia" },{ "num": 11, "over": "Mexico" },{ "num": 12, "over": "India" },];
var columnsInfo = { "num": "<span class='mainTitle KPMGWeb-ExtraLight'>Technology & innovation pillar: score by country</span>" };
$("#chart").empty();
var barChartConfig = {
mainDiv: "#chart",
colorRange: ["#0091DA", "#6D2077"],
data: groupChartData,
columnsInfo: columnsInfo,
xAxis: "runs",
yAxis: "over",
label: {
xAxis: "",
yAxis: ""
},
requireLegend: true
};
var groupChart = new horizontalGroupBarChart(barChartConfig);
.mainTitle{
font-size: 3em;
}
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="chart" style="width: 800;height: 600">
Thank you very much!
It sounds like you were almost there with adding the labels. You want to add them to the g element that holds the rect bars, and add them after the rects so they will appear on top of the bars, so:
var element = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(0," + y0(d[yAxis]) + ")";
});
var rect = element.selectAll("rect")
.data(function(d, i) {
return keys.map(function(key) {
return {
key: key,
value: d[key],
index: key + "_" + i + "_" + d[yAxis]
};
});
})
.enter().append("rect")
.attr("width", function (d) {
return x(d.value);
})
[ etc. ]
// add the text elements
element.append('text')
The text element needs to be at the end of the bar, and the bar width is x(d.value) using the data bound to the rect elements. d.value translates to d.num in terms of the data you already have attached to the g elements, so we can set the x attribute as x(d.num). If the text-anchor is set to end, that will align the end of the text with the end of the bar; we want a little space between text and end of bar, so add in a small offset:
element.append('text')
.attr('text-anchor', 'end')
.attr('x', d => x(d.num) - 5)
The value to be shown in the bar is also going to be d.num, so we can add that:
element.append('text')
.attr('text-anchor', 'end')
.attr('x', d => x(d.num) - 5)
.text(d => d.num )
If you run the code now, you'll find that the numbers are partially obscured by the bar above, so let's sort out the y offset. The bar width is y1.bandwidth(); to align the baseline of the text with the bottom of the bar, add
element.append('text')
.attr('text-anchor', 'end')
.attr('x', d => x(d.num) - 5)
.attr('y', y1.bandwidth())
.text(d => d.num )
Depending on what size you anticipate your users viewing the chart at, you might want to try to centre the text over the bar -- e.g. try
element.append('text')
.attr('text-anchor', 'end')
.attr('x', d => x(d.num) - 5)
.attr('y', y1.bandwidth()/2)
.attr('dy', '0.25em')
.text(d => d.num )
Here's a working example:
$(window).on('resize', function(event) {
$("#chart").width(window.innerWidth * 0.9);
$("#chart").height(window.innerHeight);
});
function horizontalGroupBarChart(config) {
function setReSizeEvent(data) {
var resizeTimer;
var interval = 500;
window.removeEventListener('resize', function() {});
window.addEventListener('resize', function(event) {
if (resizeTimer !== false) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(function() {
$(data.mainDiv).empty();
drawHorizontalGroupBarChartChart(data);
clearTimeout(resizeTimer);
}, interval);
});
}
drawHorizontalGroupBarChartChart(config);
setReSizeEvent(config);
}
function createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange) {
var z = d3.scaleOrdinal()
.range(colorRange);
var mainDivName = mainDiv.substr(1, mainDiv.length);
$(mainDiv).before("<div id='Legend_" + mainDivName + "' class='pmd-card-body' style='margin-top:0; margin-bottom:0;text-align:center'></div>");
var keys = Object.keys(columnsInfo);
keys.forEach(function(d) {
var cloloCode = z(d);
$("#Legend_" + mainDivName).append("<span class='team-graph team1' style='display: inline-block; margin-right:10px;'>\
<span style='background:" + cloloCode + ";width: 10px;height: 10px;display: inline-block;vertical-align: middle;'> </span>\
<span style='padding-top: 0;font-family:Source Sans Pro, sans-serif;font-size: 13px;display: inline;'>" + columnsInfo[d] + " </span>\
</span>");
});
}
function drawHorizontalGroupBarChartChart(config) {
var data = config.data;
var columnsInfo = config.columnsInfo;
var xAxis = config.xAxis;
var yAxis = config.yAxis;
var colorRange = config.colorRange;
var mainDiv = config.mainDiv;
var mainDivName = mainDiv.substr(1, mainDiv.length);
var label = config.label;
var requireLegend = config.requireLegend;
d3.select(mainDiv).append("svg").attr("width", $(mainDiv).width()).attr("height", $(mainDiv).height() * 0.80).attr("class", "mainSVG")
var svg = d3.select(mainDiv + " svg"),
margin = {
top: 20,
right: 20,
bottom: 40,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var g = svg.append("g").attr("transform", "translate(" + (margin.left * 2.3) + "," + margin.top + ")");
if (requireLegend != null && requireLegend != undefined && requireLegend != false) {
$("#Legend_" + mainDivName).remove();
createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange);
}
$(".mainSVG").attr("transform", "translate(5,10)")
var y0 = d3.scaleBand()
.rangeRound([height, 0])
.paddingInner(0.1);
var y1 = d3.scaleBand()
.padding(0.05);
var x = d3.scaleLinear()
.rangeRound([0, width - margin.left]);
var z = d3.scaleOrdinal()
.range(colorRange);
var keys = Object.keys(columnsInfo);
y0.domain(data.map(function(d) {
return d[yAxis];
}));
y1.domain(keys).rangeRound([0, y0.bandwidth()]);
x.domain([0, d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
})]).nice();
var maxTicks = d3.max(data, function(d) {
return d3.max(keys, function(key) {
return d[key];
});
});
var element = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(0," + y0(d[yAxis]) + ")";
});
var rect = element.selectAll("rect")
.data(function(d, i) {
return keys.map(function(key) {
return {
key: key,
value: d[key],
index: key + "_" + i + "_" + d[yAxis]
};
});
})
.enter().append("rect")
.attr("y", function(d) {
return y1(d.key);
})
.attr("width", function(d) {
return x(d.value);
})
.attr("data-index", function(d, i) {
return d.index;
})
.attr("height", y1.bandwidth())
.attr("fill", function(d) {
return z(d.key);
})
element.append('text')
.attr('x', d => x(d.num) - 5)
.attr('y', y1.bandwidth()/2)
.attr('dy', '0.35em')
.attr('text-anchor', 'end')
.text(d => d.num )
var datax = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var tScale = d3.scaleLinear()
.rangeRound([0, width - margin.left]);
tScale.domain(d3.extent(datax)).nice();
//CBT:add tooltips
var self = {};
self.svg = svg;
self.cssPrefix = "horgroupBar0_";
self.data = data;
self.keys = keys;
self.height = height;
self.width = width;
self.label = label;
self.yAxis = yAxis;
self.xAxis = xAxis;
horBarTooltip.addTooltips(self);
rect.on("mouseover", function() {
var currentEl = d3.select(this);
var index = currentEl.attr("data-index");
horBarTooltip.showTooltip(self, index);
});
rect.on("mouseout", function() {
var currentEl = d3.select(this);
var index = currentEl.attr("data-index");
horBarTooltip.hideTooltip(self, index);
});
rect.on("mousemove", function() {
horBarTooltip.moveTooltip(self);
});
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(maxTicks))
.append("text")
.attr("x", width / 2)
.attr("y", margin.bottom * 0.7)
.attr("dx", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y0).ticks(null, "s"))
.append("text")
.attr("x", height * 0.4 * -1)
.attr("y", margin.left * 0.8 * -1) //y(y.ticks().pop()) + 0.5)
.attr("dy", "0.71em")
.attr("fill", "#00338D")
.attr("font-weight", "bold")
// .attr("text-anchor", "start")
}
var helpers = {
getDimensions: function(id) {
var el = document.getElementById(id);
var w = 0,
h = 0;
if (el) {
var dimensions = el.getBBox();
w = dimensions.width;
h = dimensions.height;
} else {
console.log("error: getDimensions() " + id + " not found.");
}
return {
w: w,
h: h
};
}
}
var horBarTooltip = {
addTooltips: function(pie) {
var keys = pie.keys;
// group the label groups (label, percentage, value) into a single element for simpler positioning
var element = pie.svg.append("g")
.selectAll("g")
.data(pie.data)
.enter().append("g")
.attr("class", function(d, i) {
return pie.cssPrefix + "tooltips" + "_" + i
});
tooltips = element.selectAll("g")
.data(function(d, i) {
return keys.map(function(key) {
return {
key: key,
value: d[key],
index: key + "_" + i + "_" + d[pie.yAxis]
};
});
})
.enter()
.append("g")
.attr("class", pie.cssPrefix + "tooltip")
.attr("id", function(d, i) {
return pie.cssPrefix + "tooltip" + d.index;
})
.style("opacity", 0)
.append("rect")
.attr("rx", 2)
.attr("ry", 2)
.attr("x", -2)
.attr("opacity", 0.71)
.style("fill", "#000000");
element.selectAll("g")
.data(function(d, i) {
return keys.map(function(key) {
return {
key: key,
value: d[key],
index: key + "_" + i + "_" + d[pie.yAxis]
};
});
})
.append("text")
.attr("fill", function(d) {
return "#efefef"
})
.style("font-size", function(d) {
return 10;
})
.style("font-family", function(d) {
return "arial";
})
.text(function(d, i) {
var caption = "" + pie.label.xAxis + ":{value}";
return horBarTooltip.replacePlaceholders(pie, caption, i, {
value: d.value,
});
});
element.selectAll("g rect")
.attr("width", function(d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return dims.w + (2 * 4);
})
.attr("height", function(d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return dims.h + (2 * 4);
})
.attr("y", function(d, i) {
var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
return -(dims.h / 2) + 1;
});
},
showTooltip: function(pie, index) {
var fadeInSpeed = 250;
if (horBarTooltip.currentTooltip === index) {
fadeInSpeed = 1;
}
horBarTooltip.currentTooltip = index;
d3.select("#" + pie.cssPrefix + "tooltip" + index)
.transition()
.duration(fadeInSpeed)
.style("opacity", function() {
return 1;
});
horBarTooltip.moveTooltip(pie);
},
moveTooltip: function(pie) {
d3.selectAll("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
.attr("transform", function(d) {
var mouseCoords = d3.mouse(this.parentNode);
var x = mouseCoords[0] + 4 + 2;
var y = mouseCoords[1] - (2 * 4) - 2;
return "translate(" + x + "," + y + ")";
});
},
hideTooltip: function(pie, index) {
d3.select("#" + pie.cssPrefix + "tooltip" + index)
.style("opacity", function() {
return 0;
});
// move the tooltip offscreen. This ensures that when the user next mouseovers the segment the hidden
// element won't interfere
d3.select("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
.attr("transform", function(d, i) {
// klutzy, but it accounts for tooltip padding which could push it onscreen
var x = pie.width + 1000;
var y = pie.height + 1000;
return "translate(" + x + "," + y + ")";
});
},
replacePlaceholders: function(pie, str, index, replacements) {
var replacer = function() {
return function(match) {
var placeholder = arguments[1];
if (replacements.hasOwnProperty(placeholder)) {
return replacements[arguments[1]];
} else {
return arguments[0];
}
};
};
return str.replace(/\{(\w+)\}/g, replacer(replacements));
}
};
var groupChartData = [{
"num": 1,
"over": "Singapore"
}, {
"num": 1.3,
"over": "The Netherlands"
}, {
"num": 2,
"over": "United Kingdom"
}, {
"num": 2.4,
"over": "United States"
}, {
"num": 2.6,
"over": "New Zealand"
}, {
"num": 2.8,
"over": "Sweden"
}, {
"num": 3,
"over": "Canada"
}, {
"num": 3,
"over": "UAE"
}, {
"num": 4,
"over": "Australia"
}, {
"num": 4.4,
"over": "France"
}, {
"num": 5,
"over": "South Korea"
}, {
"num": 5.2,
"over": "Germany"
}, {
"num": 5.5,
"over": "Austria"
}, {
"num": 6,
"over": "Austria"
}, {
"num": 7,
"over": "Brazil"
}, {
"num": 7,
"over": "China"
}, {
"num": 8,
"over": "Japan"
}, {
"num": 10,
"over": "Russia"
}, {
"num": 11,
"over": "Mexico"
}, {
"num": 12,
"over": "India"
}, ];
var columnsInfo = {
"num": "<span class='mainTitle KPMGWeb-ExtraLight'>Technology & innovation pillar: score by country</span>"
};
$("#chart").empty();
var barChartConfig = {
mainDiv: "#chart",
colorRange: ["#0091DA", "#6D2077"],
data: groupChartData,
columnsInfo: columnsInfo,
xAxis: "runs",
yAxis: "over",
label: {
xAxis: "",
yAxis: ""
},
requireLegend: true
};
var groupChart = new horizontalGroupBarChart(barChartConfig);
.mainTitle {
font-size: 3em;
}
svg text {
font-size: 10px;
font-family: sans-serif;
}
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="chart" style="width: 800;height: 600">
By the way, I noticed that there seem to be two bars on the same line for Austria - not sure if that is deliberate or not.

Incomplete line chart with missing points and missing lines

var maindata=[
{"date":"21-APR-16 04:19 AM","Delhi":30,"Mumbai":28},
{"date":"21-APR-16 05:19 AM","Delhi":32,"Mumbai":30},
{"date":"21-APR-16 06:19 AM","Delhi":34,"Mumbai":34},
{"date":"21-APR-16 10:19 AM","Kolkata":34},
{"date":"21-APR-16 11:19 AM","Delhi":48,"Chennai":40,"Kolkata":36}
];
var that = this;
var deepClonedCopy = jQuery.extend(true, {}, maindata);
var data;
data = $.map(deepClonedCopy, function(el) { return el });
// var passedheight = this.getHeight();
//var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
var margin = {top: 15, right: 30, bottom: 20, left: 30},
width = 960- margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
if(data.length >= 1){
//alert(JSON.stringify(data));
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
var maxDate = d3.time.hour.offset(parseDate(d3.max(data, function(d) { return d.date; })),+1);
var minDate = d3.time.hour.offset(parseDate(d3.min(data, function(d) { return d.date; })),-1);
var div = d3.select("#toolTip");
var x = d3.time.scale()
.domain([minDate, maxDate])
.range([0, width]);
var y = d3.scale.linear()
.range([Math.ceil(height), 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").ticks(4);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4).tickSize(-width, 0, 0);
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.tonneValue); });
var svg = d3.select("body").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 + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
//x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
//d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
0,
Math.ceil(d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); }))
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var wsf = svg.selectAll(".wsf")
.data(wsfs)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
wsf.selectAll("dot")
.data(function(d) { return d.values;})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.tonneValue); })
.attr("stroke", function (d) {
return color(this.parentNode.__data__.name) })
.attr("fill", function (d) {
return color(this.parentNode.__data__.name) })
//.attr("fill-opacity", .5)
//.attr("stroke-width", 2)
.on("mouseover", function (d) {
formatDate = d3.time.format("%d-%m-%Y %H:%M %p");
div.transition().duration(100).style("opacity", .9);
div.html(/*this.parentNode.__data__.name + "<br/>" +*/ d.tonneValue /*+ "<br/>" + "<br/>"*/ +" Tonne handled at "+ formatDate(d.date))
.style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").attr('r', 8);
d3.select(this).attr('r', 8)
}).on("mouseout", function (d) {
div.transition().duration(600).style("opacity", 0)
d3.select(this).attr('r', 3);
});
var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });
data.forEach(function(d) {
d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
});
var legend = svg.selectAll(".legend")
.data(legendNames.slice())
.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", 4)
.style("fill", function(d) {return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
The above code gives 2 lines, until first 3 json elements till the data format is similar.
but from 4th element there is no Delhi / Mumbai and there is just Kolkata, but I don't find a line for Kolkata 34, Kolkata 36 neither Delhi's 48 point, where Delhi's line should extend from 34 to 48 and Chennai a simple dot is expected as there is only one point of Chennai i.e. 40.
Can any one tell where am I doing wrong. Here is a fiddle.
The main problem in the code (missing lines and points and incomplete legend) is that your data needs some structuring.
You dataset some times has cities which may or may not be present.
So The first task will be to make a proper data set
From this:
var maindata = [{
"date": "21-APR-16 04:19 AM",
"Delhi": 30,
"Mumbai": 28
}, {
"date": "21-APR-16 05:19 AM",
"Delhi": 32,
"Mumbai": 30
}, {
"date": "21-APR-16 06:19 AM",
"Delhi": 34,
"Mumbai": 34
}, {
"date": "21-APR-16 10:19 AM",
"Kolkata": 34
}, {
"date": "21-APR-16 11:19 AM",
"Delhi": 48,
"Chennai": 40,
"Kolkata": 36
}];
To this:
[
{
"name":"Delhi",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":30
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":32
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":48
}
]
},
{
"name":"Mumbai",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":28
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":30
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
}
]
},
{
"name":"Kolkata",
"values":[
{
"date":"2016-04-21T04:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":36
}
]
},
{
"name":"Chennai",
"values":[
{
"date":"2016-04-21T05:49:00.000Z",
"value":40
}
]
}
]
So now all cities have their respective data.
Number of cities x so number of lines will be x.
To make the dataset do this:
var cities = []
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
maindata.forEach(function(d) {
cities.push(d3.keys(d).filter(function(key) {
return key !== "date" && key !== "maxLength";
}));
});
cities = d3.set(d3.merge(cities)).values();//get all cites and make it unique
console.log(cities)
var myData = [];
var allValues = [];
var allDates =[];
//generate cities and its values as shown in my dataset above.
cities.forEach(function(city){
var cityData = {};
cityData.name = city;
cityData.values = [];
maindata.forEach(function(md){
if (md[city]){
allValues.push(md[city])
allDates.push(parseDate(md.date))
cityData.values.push({date: parseDate(md.date), value: md[city]})
}
})
myData.push(cityData)
});
Next make lines like this:
var wsf = svg.selectAll(".wsf")
.data(myData)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
And Legends like this:
var legend = svg.selectAll(".legend")
.data(cities)
.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", 4)
.style("fill", function(d) {
return color(d);
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
}
working code here
Here is the updated fiddle
some keys(i.e. Kolkata)aren't included in binding data
Original code:
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To(you need change ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'] to code more formally):
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})
};
});
Filter data
Original code:
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To:
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})};
});

display rectangles based on values

I am trying to draw rectangle size based on the values. I want to draw bigger rectangle if the value is more and relatively smaller size if the value is lesser.
My code:
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr("id", function(d,i) { console.log(d); return d.instancegroup; })
.attr("rx",3)
.attr("ry",3)
.style("fill", function(d,i) { return "#01DF01";})
.attr("x", function(d,i) { return i * 80; })
.attr("width",function (d,i){
if (d.value < 80)
{
return 40;
}
else
{
return d.value/4;
}
})
.on("click",function(d,i) { console.log(d);})
.attr("y", function(d) { return 40; })
.attr("height", function(d) {
if (d.value < 80)
{
return 15;
}
else
{
return d.value/5;
}
})
However the spacing between the rectangles does not work properly. Is there a better way to resolve this.
I tried with axis too but didnt work. I was getting wrong to align the x-axis.
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr("id", function(d,i) { console.log(d); return d.instancegroup; })
.attr("rx",3)
.attr("ry",3)
.style("fill", function(d,i) { return "#01DF01";})
.attr("x", function (d,i) {
if(i ==0 )
{
return 40;
}
else
{
return x(d.value) + 100 ;
}
})
.attr("y", function (d,i) { return 40;})
.attr("width", function (d,i) { return height - y(d.value);})
.attr("height", function (d,i) { console.log(x(d.value)); return height - y(d.value);})
The data :
[{"status: "Up",value: "300"},{"status: "Up",value: "200"},{"status: "Up",value: "35"}]
Since x positions are accumulated using widths of previous rectangles having varying dimensions, I'd pre-calculate these values and attach them to the objects prior to binding the data array via d3:
var SPACING = 20;
var data = [
{"status": "Up", value: "300"},
{"status": "Up", value: "200"},
{"status": "Up", value: "35"}
];
data.forEach(function(d, i, array) {
d.width = d.value < 80 ? 40 : d.value / 4;
d.height = d.value < 80 ? 15 : d.value / 5;
d.x = i == 0 ? 0 : array[i-1].x + array[i-1].width + SPACING;
d.y = 40;
});
Later on you are able to directly access these properties without the need for further calculations:
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr({
"id": function (d, i) { return d.instancegroup; },
"rx": 3,
"ry": 3,
"x": function (d) { return d.x; },
"y": function (d) { return d.y; },
"width": function (d) { return d.width; },
"height": function (d) { return d.height; }
})
.on("click", function (d) {
console.log(d);
})
.style("fill", function (d) {
return "#01DF01";
})

Rotate horizontal bar chart to vertical chart

Saw this graph on JSFiddle. I modified it with my own data, but I'd like to have a vertical bar chart instead of a horizontal. Here's what it looks like now. I tried fiddling with the X and Y axes and can't seem to get it right. Any help would be greatly appreciated.
Here's my code:
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 40
},
legendPanel = {
width: 180
},
width = 400 - margins.left - margins.right - legendPanel.width,
height = 200 - margins.top - margins.bottom,
dataset = [{
data: [{
year: '2009',
count: 80
}, {
year: '2010',
count: 79
}, {
year: '2011',
count: 65
},
{
year: '2012',
count: 70
},
{
year: '2013',
count: 72
} ,
{
year: '2014*',
count: 38
}
],
name: 'Male'
}, {
data: [{
year: '2009',
count: 15
}, {
year: '2010',
count: 17
}, {
year: '2011',
count: 18
}, {
year: '2012',
count: 20
}, {
year: '2013',
count: 17
},
{
year: '2014*',
count: 8
}],
name: 'Female'
}
],
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.year
};
});
}),
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('#sex')
.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, 200]),
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.category20c(),
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', 'white')
.attr('width', 160)
.attr('height', 30 * dataset.length)
.attr('x', width + margins.left)
.attr('y', 0);
//X AXIS LABEL
svg.append("text")
.attr("class", 'x label')
.attr("y", height + 35)
.attr("x", width / 2)
.text("Victims")
.style("font-size","13px")
.style("font-weight", "600");
/*
//FOOTNOTE
svg.append("text")
.attr("class", 'x label')
.attr("y", height + 35)
.attr("x", width + 65)
.text("*As of July 2014")
.style("font-size","11px")
.style("font-style", "italic");
*/
//LEGEND
series.forEach(function (s, i) {
svg.append('text')
.attr('fill', 'black')
.attr('x', width + margins.left + 18)
.attr('y', i * 24 + 24)
.text(s);
svg.append('rect')
.attr('fill', colours(i))
.attr('width', 20)
.attr('height', 20)
.attr('x', width + margins.left + 75)
.attr('y', i * 24 + 6);
});

Resources