Find location of y-axis ticks using d3.js - javascript

I am building a SVG visualisation using d3.js (in PowerBI - so version 3 of d3), and I am struggling aligning my data points and fixed lines with the appropriate y-axis tick marker.
For example, with 8 axis points, the lines are almost right, just slightly above
But when there is only 1 or 2 points, it's way off
I am trying to dynamically calculate the offset as the number of y-axis ticks will depend on the PowerBI filter I have.
My current calculation is that I am taking the height of the svg, dividing it by the number of ticks, and then dividing that by two so it lands in the centre. The y-axis is ordinal.
Relevant code is:
var margin = {top: 20, right: 250, bottom: 50, left: 50},
width = pbi.width - margin.left - margin.right,
height = pbi.height - margin.top - margin.bottom,
legendleft = pbi.width - margin.right;
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], barPad, barOuterPad);
var svg = d3.select("#chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + legendleft)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
y.domain(Data.map(function(d) { return d.reportingyear; }));
var YearSize = new Set(yearArray).size // Gets the number of ticks on the y-axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0, 6)")
.call(d3.svg.axis().scale(y).orient("left"));
// Chart the rows
var publishedRow = svg.append("g").attr("id", "publishedgroup").selectAll(null)
.data(rowArray)
.enter();
publishedRow.append("line")
.style("stroke", "grey")
.style("stroke-width", 1)
.style("stroke-dasharray", ("2, 2"))
.attr("x1", function(d) { return 0; })
.attr("y1", function(d) { return y(d.entry)+((pbi.height-margin.top-margin.bottom) / (new Set(yearArray).size) / 2); })
.attr("x2", function(d) { return width; })
.attr("y2", function(d) { return y(d.entry)+((pbi.height-margin.top-margin.bottom) / (new Set(yearArray).size) / 2); });
publishedRow.append("circle")
.attr("cx", function(d) {return x(new Date(d.date))} )
.attr("cy", function(d) {return y(d.year)+((pbi.height-margin.top-margin.bottom) / (new Set(yearArray).size) / 2); })
.attr("r", 7)
.style("fill", function(d, i) { return milestoneMap[d.milestone]; })
});
It is the .attr lines that have the code that dynamically calculates the offset.
Is there an easier way to do this? Or can I get some advice as to why my calculation isn't working?
Thank you!

I should have used
.rangePoints rather than .rangeRoundBands
Then a static offset of 6 worked.
Problem solved

Related

Line Chart path in D3 JS not displaying Properly

I am trying to implement a line chart through this function. when I try to add the path, the path alone is not getting displayed. any help would be appreciated. (last 10 lines of the code add the path to the graph)
function drawLineChart(country) {
console.log(countryValueMap[country])
var margin = {top: 60, right: 110, bottom: 50, left: 50},
width = 800 - margin.left - margin.right + 50,
height = 500 - margin.top - margin.bottom + 50;
// document.getElementById("my_dataviz").innerHTML = "";
const lineSvg = d3.select("#linechart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
const x = d3.scaleTime()
.range([0 , width])
.domain([new Date(1958,0,1), new Date(2011,0,1)])
lineSvg.append("g")
.attr("transform", `translate(0, ${height})`)
.call(d3.axisBottom(x).ticks(5).tickFormat(d => d.getFullYear()))
const y = d3.scaleLinear()
.domain([0,countryMaxValueMap[country]])
.range([height, 0])
lineSvg.append("g")
.call(d3.axisRight(y).tickSize(width)).call(g => g.select(".domain")
.remove())
.call(g => g.selectAll(".tick:not(:first-of-type) line")
.attr("stroke-opacity", 0.5)
.attr("stroke-dasharray", "2,2"))
.call(g => g.selectAll(".tick text")
.attr("x", 4)
.attr("dy", -4));
lineSvg.append("path")
.datum(countryValueMap[country])
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(function(d) { return x(d.year) })
.y(function(d) { return y(d.gdp) })
)
}
When I try to execute the above code, this is the output I am getting.
I tried logging the value of d3.year and d3.gdp and the variable works fine.
Given that the line is at 1970 (the start of epoch time), it looks like something is wrong with the how the years are represented in your data. Since you are using a scaleTime, d.year must be a date object. It can't just be an integer representing the year.

D3js Grouped Scatter plot with no collision

I am using this example to make scatter plot:
https://www.d3-graph-gallery.com/graph/boxplot_show_individual_points.html
Now this example uses jitter to randomize x position of the dots for demonstration purpose, but my goal is to make these dots in that way so they don't collide and to be in the same row if there is collision.
Best example of what I am trying to do (visually) is some sort of beeswarm where data is represented like in this fiddle:
https://jsfiddle.net/n444k759/4/
Snippet of first example:
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 30, left: 40},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {
// Compute quartiles, median, inter quantile range min and max --> these info are then used to draw the box.
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) { return d.Species;})
.rollup(function(d) {
q1 = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.25)
median = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.5)
q3 = d3.quantile(d.map(function(g) { return g.Sepal_Length;}).sort(d3.ascending),.75)
interQuantileRange = q3 - q1
min = q1 - 1.5 * interQuantileRange
max = q3 + 1.5 * interQuantileRange
return({q1: q1, median: median, q3: q3, interQuantileRange: interQuantileRange, min: min, max: max})
})
.entries(data)
// Show the X scale
var x = d3.scaleBand()
.range([ 0, width ])
.domain(["setosa", "versicolor", "virginica"])
.paddingInner(1)
.paddingOuter(.5)
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
// Show the Y scale
var y = d3.scaleLinear()
.domain([3,9])
.range([height, 0])
svg.append("g").call(d3.axisLeft(y))
// Show the main vertical line
svg
.selectAll("vertLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d){return(x(d.key))})
.attr("x2", function(d){return(x(d.key))})
.attr("y1", function(d){return(y(d.value.min))})
.attr("y2", function(d){return(y(d.value.max))})
.attr("stroke", "black")
.style("width", 40)
// rectangle for the main box
var boxWidth = 100
svg
.selectAll("boxes")
.data(sumstat)
.enter()
.append("rect")
.attr("x", function(d){return(x(d.key)-boxWidth/2)})
.attr("y", function(d){return(y(d.value.q3))})
.attr("height", function(d){return(y(d.value.q1)-y(d.value.q3))})
.attr("width", boxWidth )
.attr("stroke", "black")
.style("fill", "#69b3a2")
// Show the median
svg
.selectAll("medianLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d){return(x(d.key)-boxWidth/2) })
.attr("x2", function(d){return(x(d.key)+boxWidth/2) })
.attr("y1", function(d){return(y(d.value.median))})
.attr("y2", function(d){return(y(d.value.median))})
.attr("stroke", "black")
.style("width", 80)
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.Species); }))
// .force("y", d3.forceX(function(d) { return y(d.Sepal_lenght) }))
.force("collide", d3.forceCollide()
.strength(1)
.radius(4+1))
.stop();
for (var i = 0; i < data.length; ++i) simulation.tick();
// Add individual points with jitter
var jitterWidth = 50
svg
.selectAll("points")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){return( d.x )})
.attr("cy", function(d){return(y(d.Sepal_Length))})
.attr("r", 4)
.style("fill", "white")
.attr("stroke", "black")
})
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
I tried to make something like this:
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.Species); }))
.force("collide", d3.forceCollide(4)
.strength(1)
.radius(4+1))
.stop();
for (var i = 0; i < 120; ++i) simulation.tick();
// Append circle points
svg.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d){
return(x(d.x))
})
.attr("cy", function(d){
return(y(d.y))
})
.attr("r", 4)
.attr("fill", "white")
.attr("stroke", "black")
but it does not even prevent collision and I am a bit confused with it.
I also tried to modify plot from this example:
http://bl.ocks.org/asielen/92929960988a8935d907e39e60ea8417
where beeswarm looks exactly what I need to achieve. But this code is way too expanded as it is made to fit the purpose of reusable charts and I can't track what exact formula is used to achieve this:
Any help would be great..
Thanks
Here's a quick example which combines the ideas of your beeswarm example with your initial boxplot. I've commented the tricky parts below:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>
<script>
// set the dimensions and margins of the graph
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 460 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// append the svg object to the body of the page
var svg = d3.select("#my_dataviz")
.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 + ")");
// Read the data and compute summary statistics for each specie
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/iris.csv", function(data) {
// Compute quartiles, median, inter quantile range min and max --> these info are then used to draw the box.
var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
.key(function(d) {
return d.Species;
})
.rollup(function(d) {
q1 = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .25)
median = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .5)
q3 = d3.quantile(d.map(function(g) {
return g.Sepal_Length;
}).sort(d3.ascending), .75)
interQuantileRange = q3 - q1
min = q1 - 1.5 * interQuantileRange
max = q3 + 1.5 * interQuantileRange
return ({
q1: q1,
median: median,
q3: q3,
interQuantileRange: interQuantileRange,
min: min,
max: max
})
})
.entries(data)
// Show the X scale
var x = d3.scaleBand()
.range([0, width])
.domain(["setosa", "versicolor", "virginica"])
.paddingInner(1)
.paddingOuter(.5)
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
// Show the Y scale
var y = d3.scaleLinear()
.domain([3, 9])
.range([height, 0])
svg.append("g").call(d3.axisLeft(y))
// Show the main vertical line
svg
.selectAll("vertLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d) {
return (x(d.key))
})
.attr("x2", function(d) {
return (x(d.key))
})
.attr("y1", function(d) {
return (y(d.value.min))
})
.attr("y2", function(d) {
return (y(d.value.max))
})
.attr("stroke", "black")
.style("width", 40)
// rectangle for the main box
var boxWidth = 100
svg
.selectAll("boxes")
.data(sumstat)
.enter()
.append("rect")
.attr("x", function(d) {
return (x(d.key) - boxWidth / 2)
})
.attr("y", function(d) {
return (y(d.value.q3))
})
.attr("height", function(d) {
return (y(d.value.q1) - y(d.value.q3))
})
.attr("width", boxWidth)
.attr("stroke", "black")
.style("fill", "#69b3a2")
// Show the median
svg
.selectAll("medianLines")
.data(sumstat)
.enter()
.append("line")
.attr("x1", function(d) {
return (x(d.key) - boxWidth / 2)
})
.attr("x2", function(d) {
return (x(d.key) + boxWidth / 2)
})
.attr("y1", function(d) {
return (y(d.value.median))
})
.attr("y2", function(d) {
return (y(d.value.median))
})
.attr("stroke", "black")
.style("width", 80)
var r = 8;
// create a scale that'll return a discreet value
// so that close y values fall in a line
var yPtScale = y.copy()
.range([Math.floor(y.range()[0] / r), 0])
.interpolate(d3.interpolateRound)
.domain(y.domain());
// bucket the data
var ptsObj = {};
data.forEach(function(d,i) {
var yBucket = yPtScale(d.Sepal_Length);
if (!ptsObj[d.Species]){
ptsObj[d.Species] = {};
}
if (!ptsObj[d.Species][yBucket]){
ptsObj[d.Species][yBucket] = [];
}
ptsObj[d.Species][yBucket].push({
cy: yPtScale(d.Sepal_Length) * r,
cx: x(d.Species)
});
});
// determine the x position
for (var x in ptsObj){
for (var row in ptsObj[x]) {
var v = ptsObj[x][row], // array of points
m = v[0].cx, // mid-point
l = m - (((v.length / 2) * r) - r/2); // left most position based on count of points in the bucket
v.forEach(function(d,i){
d.cx = l + (r * i); // x position
});
}
}
// flatten the data structure
var flatData = Object.values(ptsObj)
.map(function(d){return Object.values(d)})
.flat(2);
svg
.selectAll("points")
.data(flatData)
.enter()
.append("circle")
.attr("cx", function(d) {
return d.cx;
})
.attr("cy", function(d) {
return d.cy;
})
.attr("r", 4)
.style("fill", "white")
.attr("stroke", "black")
})
</script>
</body>
</html>

D3 JS: How to add transition to refresh a plot when the dataset x-scale changes?

Looking at this Histogram chart using d3 example I plugged in my data but it had some strange side effects e.g. after refreshing to a new dataset, some information from the previous dataset i.e. x-axis scale was retained. I tried deleting and appending a new x-axis etc but nothing worked.
This happened due to the fact that my datasets had completely different x-axis ranges and scales. The only way I found to make it work was to select the whole svg element, remove it and re-append everything anew. However, this doesn't make a pleasant transition for the user so I was wondering how can this be improved to make it refreshable using transitions as in the original example even when having datasets with different x-scales and ranges.
This was my last approach which is a bit harsh to the eye:
// delete old
d3.select("#" + divId).select("svg").remove();
// then recreate all new
And this was my refresh attempt (integrated with AngularJS). Note how it has some common initialization and then if the SVG doesn't exist appends everything new otherwise tries to update it. I went bit by bit but can't see why the refresh doesn't remove all the previous dataset information of the x-axis scale:
var divId = $scope.histogramData.divId;
var color = $scope.histogramData.color;
var values = $scope.histogramData.data[$scope.histogramData.selected];
var svg = $scope.histogramData.svg;
// plot common initialization
var margin = {top: 40, right: 20, bottom: 20, left: 20},
width = 450 - margin.left - margin.right,
height = 370 - margin.top - margin.bottom;
var max = d3.max(values);
var min = d3.min(values);
var x = d3.scale.linear()
.domain([min, max])
.range([0, width]);
// generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(x.ticks(10))
(values);
var yMax = d3.max(data, function(d){ return d.length });
var yMin = d3.min(data, function(d){ return d.length });
var colorScale = d3.scale.linear()
.domain([yMin, yMax])
.range([d3.rgb(color).brighter(), d3.rgb(color).darker()]);
var y = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
// ===================================================================
// If the SVG doesn't exist then adds everything new
// ===================================================================
if (svg === undefined) {
var svg = d3.select("#" + divId)
.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 + ")");
$scope.histogramData.svg = svg;
var bar = svg.selectAll(".bar")
.data(data)
.enter()
.append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", (x(data[0].dx) - x(0)) - 1)
.attr("height", function(d) { return height - y(d.y); })
.attr("fill", function(d) { return colorScale(d.y) });
bar.append("text")
.attr("dy", ".75em")
.attr("y", -12)
.attr("x", (x(data[0].dx) - x(0)) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
var gTitle = svg.append("text")
.attr("x", 0)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "left")
.classed("label", true)
.text($scope.histogramData.spec[selected]);
$scope.histogramData.gTitle = gTitle;
var gAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
$scope.histogramData.gAxis = gAxis;
} else {
// ===================================================================
// If the SVG does exist then tries refreshing
// ===================================================================
var bar = svg.selectAll(".bar").data(data);
// remove object with data
bar.exit().remove();
bar.transition()
.duration(1000)
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
bar.select("rect")
.transition()
.duration(1000)
.attr("height", function(d) { return height - y(d.y); })
.attr("fill", function(d) { return colorScale(d.y) });
bar.select("text")
.transition()
.duration(1000)
.text(function(d) { return formatCount(d.y); });
var gTitle = $scope.histogramData.gTitle;
gTitle.transition()
.duration(1000)
.text($scope.histogramData.spec[selected]);
var gAxis = $scope.histogramData.gAxis;
gAxis.transition()
.duration(1000)
.call(xAxis);
}
I would suggest to keep this d3 code inside one angularJS directive and keep a watch on the json which you are using to plot that graph. As soon as values are changing the directive will be called again and the graph will be plotted. Hope it helps.

Unneeded white space before the 1st bar in D3 Stack chart

I am trying to populate a data set into D3's Bar chart data. I am using this example from the d3:
https://bl.ocks.org/mbostock/1134768
var causes = ["wounds", "other", "disease"];
var parseDate = d3.time.format("%m/%Y").parse;
var margin = {top: 20, right: 50, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
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 + ")");
d3.tsv("data.csv", function(error, crimea) {
if (error) throw error;
var layers = d3.layout.stack()(causes.map(function(c) {
return crimea.map(function(d) {
return {x: parseDate(d.date), y: +d[c]};
});
}));
var x = d3.scale.ordinal()
.domain([0,1])
.rangeRoundBands([0, width], 0.1, 0);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var z = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%b"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
console.log(layers);
x.domain(layers[0].map(function(d) { return d.x; }));
y.domain([0, d3.max(layers[layers.length - 1], function(d) { return d.y0 + d.y; })]).nice();
var ticks = x.domain().filter(function(d,i){ return !(i%20); } );
xAxis.tickValues( ticks );
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return z(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() - 1);
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(" + 0 + ",0)")
.call(yAxis);
});
This issue I am getting is that I have some white space which looks ugly. This space come before the 1st bar and also after the last bar of the chart. I have tried tweaking the x value of the bar, But I think that is not a good way to do.
This space does not come when the data set is small. But when dataset is large then this space comes up. How can I remove this space from the start and from end.
JSFiddle For the Above code is
https://jsfiddle.net/7qnngbdc/
See here --> https://github.com/mbostock/d3/wiki/Ordinal-Scales#ordinal_rangeRoundBands
"Note that rounding necessarily introduces additional outer padding which is, on average, proportional to the length of the domain. For example, for a domain of size 50, an additional 25px of outer padding on either side may be required. Modifying the range extent to be closer to a multiple of the domain length may reduce the additional padding."
After you've set the domain, try this -->
var mult = Math.max (1, Math.floor (width / x.domain().length));
x.rangeRoundBands ([0, (x.domain().length * mult)], 0.1, 0);
Changed in https://jsfiddle.net/7qnngbdc/1/

D3 bar char x-axis line not displaying

I am trying to draw a line in x-axis (bottom of bars in the chart) using the following script but it draws the on the top. What is the correct way of adding line on the bottom? Please help me to solve it.
var datasetBarChart = ${barList};
// set initial group value
var group = "All";
function datasetBarChosen(group) {
var ds = [];
for (x in datasetBarChart) {
if (datasetBarChart[x].group == group) {
ds.push(datasetBarChart[x]);
}
}
return ds;
}
function dsBarChartBasics() {
var margin = {top: 30, right: 5, bottom: 20, left: 50},
width = 1000 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom,
colorBar = d3.scale.category20(),
barPadding = 1
;
return {
margin: margin,
width: width,
height: height,
colorBar: colorBar,
barPadding: barPadding
}
;
}
function dsBarChart() {
var firstDatasetBarChart = datasetBarChosen(group);
var basics = dsBarChartBasics();
var margin = basics.margin,
width = basics.width,
height = basics.height,
colorBar = basics.colorBar,
barPadding = basics.barPadding
;
var xScale = d3.scale.linear()
.domain([0, firstDatasetBarChart.length])
.range([0, width])
;
// Create linear y scale
// Purpose: No matter what the data is, the bar should fit into the svg area; bars should not
// get higher than the svg height. Hence incoming data needs to be scaled to fit into the svg area.
var yScale = d3.scale.linear()
// use the max funtion to derive end point of the domain (max value of the dataset)
// do not use the min value of the dataset as min of the domain as otherwise you will not see the first bar
.domain([0, d3.max(firstDatasetBarChart, function (d) {
return d.measure;
})])
// As coordinates are always defined from the top left corner, the y position of the bar
// is the svg height minus the data value. So you basically draw the bar starting from the top.
// To have the y position calculated by the range function
.range([height, 0])
;
//Create SVG element
var svg = d3.select("#barChart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "barChartPlot")
;
var plot = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
;
var median = svg.append("line")
.attr("x2", width)
.attr("y2", (xScale/width))
.attr("stroke-width", 2)
.attr("stroke", "black");
plot.selectAll("rect")
.data(firstDatasetBarChart)
.enter()
.append("rect")
.attr("x", function (d, i) {
return xScale(i);
})
.attr("width", width / firstDatasetBarChart.length - barPadding)
.attr("y", function (d) {
return yScale(d.measure);
})
.attr("height", function (d) {
return height - yScale(d.measure);
})
.attr("fill", "lightgrey")
;
// Add y labels to plot
plot.selectAll("text")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function (d) {
return formatAsInteger(d3.round(d.measure));
})
.attr("text-anchor", "middle")
// Set x position to the left edge of each bar plus half the bar width
.attr("x", function (d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", function (d) {
return yScale(d.measure) + 14;
})
.attr("class", "yAxis")
/* moved to CSS
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
*/
;
// Add x labels to chart
var xLabels = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + (margin.top + height) + ")")
;
xLabels.selectAll("text.xAxis")
.data(firstDatasetBarChart)
.enter()
.append("text")
.text(function (d) {
return d.category;
})
.attr("text-anchor", "middle")
// Set x position to the left edge of each bar plus half the bar width
.attr("x", function (d, i) {
return (i * (width / firstDatasetBarChart.length)) + ((width / firstDatasetBarChart.length - barPadding) / 2);
})
.attr("y", 15)
.attr("class", "xAxis")
//.attr("style", "font-size: 12; font-family: Helvetica, sans-serif")
;
// Title
svg.append("text")
.attr("x", (width + margin.left + margin.right) / 2)
.attr("y", 15)
.attr("class", "title")
.attr("text-anchor", "middle")
.text Breakdown 2015")
;
}
dsBarChart();
script for the line;
var median = svg.append("line")
.attr("x2", width)
.attr("y2", (xScale/width))
.attr("stroke-width", 2)
.attr("stroke", "black");
I don't quite understand your y2 attribute. It looks like you want the line to render as <line x1="0" y1="{height}" x2="{width}" y2="{height}" />
Ideally you want to express this in terms of your scale functions so if they change you won't have to update this statement. The corresponding d3 call for that would be:
var median = svg.append("line")
.attr("stroke-width", 2)
.attr("stroke", "black")
.attr("x1", xScale.range()[0])
.attr("x2", xScale.range()[1])
.attr("y1", yScale.range()[0])
.attr("y2", yScale.range()[0]);
Also, I think something is up with the xScale/width calculation. xScale is a function. Though you should look into d3.svg.axis too

Categories

Resources