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
Related
I have drawn the following chart with D3 Charting tool v4. I have attached the full code at the bottom of this post.
The red line is the target goal to be achieved. The following code block is drawing this line:
var targetGoalArr = [7];
svg.selectAll(".targetgoal")
.data(targetGoalArr)
.enter().append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#cc0000");
Now I need to label this line with the text Growth Target (7) to the right of it and in two lines. The label has to be broken in two lines as well!
The following screenshot shows the desired output.
How can I achieve the above?
One more thing I am not able to draw is the Y-Axis baseline. In my chart (with red line) I am creating the horizontal lines using a custom tick array. Here is the code:
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
svg.append("g")
.attr("class", "grid axis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);
However, if I do not use custom ticks for Y-Axis, the baseline appears but I am missing the horizontal grid lines. I have to display both at the same time.
Here is my full code:
public function evd_unitary_growth_plan_chart( $data_str ){
ob_start(); ?>
<style> /* set the CSS */
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.5;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
.axis {
font-size: 13px;
font-family: 'Roboto';
color: #808888;
}
</style>
<script type="text/javascript">
var h = 300;
var w = 750;
var barPadding = 2;
function barColor(data_month, current_month) {
if( parseInt(data_month) >= current_month)
return "#008600";
else
return "#c4c4c4";
}
// set the dimensions and margins of the graph
var margin = {top: 30, right: 20, bottom: 30, left: 40},
width = w - margin.left - margin.right,
height = h - margin.top - margin.bottom;
var data = <?php echo $data_str ?>;
// set the ranges
var x = d3.scaleBand().range([0, width]).padding(0.2);
var y = d3.scaleLinear().range([height, 0]);
var svg = d3.select("#ecbg_unitary").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 + ")");
// Scale the range of the data in the domains
x.domain(data.map(function(d) { return d.month; }));
var y_domain_upperBound = d3.max(data, function(d) { return d.points; });
y_domain_upperBound = Math.round(y_domain_upperBound / 10) * 10 + 10;
y.domain([0, y_domain_upperBound]);
// Create Y-Axis tick array to draw grid lines
var yTicks = [];
var tickInterval = 5;
for(var i = 0; i <= y_domain_upperBound; i = i + tickInterval) {
yTicks.push(i);
}
console.log(yTicks);
// gridlines in y axis function
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
// Reference line - The red line
var targetGoalArr = [7];
svg.selectAll(".targetgoal")
.data(targetGoalArr)
.enter().append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#cc0000");
// append the rectangles for the bar chart
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return x(d.month);
})
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.points); })
.attr("height", function(d) { return height - y(d.points); })
.attr("fill", function(d){return barColor(d.data_month_number, d.current_month_number)});
// column labels
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.points;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return x(d.month) + x.bandwidth() / 2;
})
.attr("y", function(d) {
return y(d.points) - 10;
})
.attr("font-family", "Roboto")
.attr("font-size", "13px")
.attr("font-weight", "bold")
.attr("fill", "#606668");
// add the x Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the Y gridlines
svg.append("g")
.attr("class", "grid axis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);
</script>
<?php return ob_get_clean();
}
To add a label to your target line, you are best to create group (g) element, and then append a line and text element to it. The g element can be translated to the correct y position, so that the line and text can be positioned relatively to the g.
var targetGoalArr = [7];
var target = g.selectAll(".targetgoal")
.data(targetGoalArr)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(0, " + y(d) +")"
})
target.append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", 0) //these can be omitted
.attr("y2", 0)
.style("stroke", "#cc0000");
target.append("text")
.text(function(d){ return "Target growth: " + d })
.attr("x", width)
.attr("y", "0.35em")
I am in the process of making a simple set of boxplots on a graph, but for some reason the line elements are not showing up
Code is:
var margin = {
top: 10,
right: 30,
bottom: 30,
left: 40
},
width = 900 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var svgBox = 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 + ")");
var yScale = d3.scaleLinear()
.range([height, 0]);
var xScale = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var center = 200
var width = 100
d3.csv("boxPlotData.csv", function(dataset) {
var max = d3.max(dataset, function(d) {
return +d.max;
});
yScale.domain([0, max])
xScale.domain(dataset.map(function(d) {
return d.borough;
}));
svgBox.append("g").call(d3.axisLeft(yScale));
svgBox.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xScale));
svgBox.selectAll("line").data(dataset).enter()
.append("line")
.attr("x1", d => xScale(d.borough) + width / 3.5)
.attr("x2", d => xScale(d.borough) + width / 3.5)
.attr("y1", d => yScale(+d.min))
.attr("y2", d => yScale(+d.max))
.attr("stroke", "black");
svgBox.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", d => xScale(d.borough) + width / 3.5)
.attr("y", d => yScale(+d.q3))
.attr("height", d => (yScale(+d.q1) - yScale(+d.q3)))
.attr("width", width)
.attr("stroke", "black")
.style("fill", "#69b3a2");
});
And my data is of the form
Data CSV
The code is entering the "rect" elements as expected, but the line components aren't showing up anywhere in the html?
The issue is you cannot just use svgBox.selectAll("line") because it will select the axis tick marks as well as the axis lines. Instead, I suggest add a class to your line with attr("class", "line") and use svgBox.selectAll(".line") to specifically select lines to append.
So the line append snippet should be:
svgBox.selectAll(".line")
.data(dataset)
.enter()
.append("line")
.attr("class", "line")
.attr("x1", d => xScale(d.borough) + width / 3.5)
.attr("x2", d => xScale(d.borough) + width / 3.5)
.attr("y1", d => yScale(+d.min))
.attr("y2", d => yScale(+d.max))
.attr("stroke", "black");
Here is the working snippet Block: https://bl.ocks.org/akulmehta/4b29fb357ea7f02a1b47b611e03a5468/
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>
I'm having a problem getting the axes to actually show on my bar graph, so far without any luck as I just can't seem to wrap my head around what's wrong. Is it something to do with the scaling?
Is the axis rendering but being cut out of the svg by the bars?
<script type="text/javascript">
//Width and height
var w = 850;
var h = 650;
var barpadding = 20;
var dataset = [40, 99];
//Create SVG element
var svg = d3.select(".wrapper")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create scale functions
var x = d3.scaleBand()
.range([0, w])
.padding(0.1);
var y = d3.scaleLinear()
.range([h, 0]);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function (d, i) {
return i * (w / dataset.length);
})
.attr("y", function (d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barpadding)
.attr("height", function (d) {
return d * 4;
})
.attr("fill", "dodgerblue");
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function (d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function (d, i) {
return i * (w / dataset.length) + (w / dataset.length - barpadding) / 2;
})
.attr("y", function (d) {
return h - (d * 4) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "18px")
.attr("fill", "black");
d3.select(".axis")
.call(d3.axisBottom(x));
</script>
For showing the axis, you'll have to append a <g> element first. After that, since the axes are always generated at the origin (0,0), you'll have to translate it down and, only then, calling the axis:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + someValue + ")")
.call(d3.axisBottom(x));
I normally provide a working demo, but this time I'll skip it, because your code has some problems:
It lacks the margins for the axis
It lacks the domains of the scales
It position the bars using magic numbers, not the scales
But, if you want to see your code with the axis 20px above the bottom, this is how it looks like: https://jsfiddle.net/qcnako3g/
I want to assign two colors to two series in my data so that when I sort ascending or descending, the colors remain the same for each group.
The data is pulled programmatically from web map features. I thought I could get away with using an alternating function to assign colors but this doesn't work if I use sorting. I am trying to find a proper way to assign colors specifically to the series.
The data I am using has OL and NOL as two groups. You can see below how it is structured.
jsfiddle
relevant code:
var values = feature.properties;
var data = [
{name:"N11OL",value:values["N11OL"]},
{name:"N11NOL",value:values["N11NOL"]},
{name:"N21OL",value:values["N21OL"]},
{name:"N21NOL",value:values["N21NOL"]},
{name:"N22OL",value:values["N22OL"]},
{name:"N22NOL",value:values["N22NOL"]},
{name:"N23OL",value:values["N23OL"]},
{name:"N23NOL",value:values["N23NOL"]},
{name:"N31_33OL",value:values["N31_33OL"]},
{name:"N31_33NOL",value:values["N31_33NOL"]},
{name:"N41OL",value:values["N41OL"]},
{name:"N41NOL",value:values["N41NOL"]}
];
var Colors = ["#a6cee3", "#1f78b4"]
var margin = {top: 40, right: 2, bottom: 30, left: 180},
width = 400 - margin.left - margin.right,
height = 575 - margin.top - margin.bottom,
barHeight = height / data.length;
// Scale for X axis
var x = d3.scale.linear()
.domain([0, d3.max(data, function(d){return d.value;})])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(["NAICS11", "NAICS21", "NAICS22", "NAICS23", "NAICS31-33", "NAICS41"])
.rangeRoundBands([0, height]);
//y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.outerTickSize(0);
var svg = d3.select(div).select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.classed("chart", true);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var bar = svg.selectAll("g.bar")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", function(d){return x(d.value);})
.attr("fill", function(d, i) {return Colors[i % 2]; }) //Alternate colors
.attr("height", barHeight - 1);
bar.append("text")
.attr("class", "text")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.attr("fill", "white")
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("text-anchor", "end");
svg.append("text")
.attr("class", "title")
.attr("x", width/7)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text("Employment by industry " + "(Total OL: " + feature.properties.IndOLTot + ")");
Starting at Line 241 in your jsFiddle
bar.append("rect")
.attr("width", function(d){return x(d.value);})
.attr("fill", function(d, i) {
if(d.name.indexOf("NOL") > -1) {
//return Colors[0];
return "red";
} else {
//return Colors[1];
return "black";
}
}) //Alternate colors
.attr("height", barHeight - 1);
This checks the name property for the substring "NOL". If the name contains "NOL" it uses the first color, if "NOL" is not found it uses the second color for a fill.
(I'm under the assumption that the series is determined by the name)