I am making a line chart using the example here.
I am trying to change the axis tick label color but "fill" is not working. I don't want to change it using CSS, it has to be changed using D3.
I tried the following on the appropriate axis section:
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.attr("stroke", "yellow")
.style("fill", "yellow")
.style("font-size", "14px")
.call(d3.axisBottom(x));
The stroke and font-size change perfectly, but the fill has no effect. I tried changing .style to .attr but still nothing.
Here is the FIDDLE. Line 45 in the HTML I set it to green with no effect.
You want to change the color of x axis tick labels, so you need to select those labels and apply the fill value to it.
You can write the below code and accomplish your task, select all x-axis text and apply color to it.
d3.selectAll('g.axis--x text')
.style('fill', 'green')
Please find the complete working instance below:
data = [
{"year" : "2005", "value": 771900},
{"year" : "2006", "value": 771500},
{"year" : "2007", "value": 770500},
{"year" : "2008", "value": 770400},
{"year" : "2009", "value": 771000},
{"year" : "2010", "value": 772400},
{"year" : "2011", "value": 774100},
{"year" : "2012", "value": 776700},
{"year" : "2013", "value": 777100},
{"year" : "2014", "value": 779200},
{"year" : "2015", "value": 782300}
]
make_graph(data)
body {
background-color: #F1F3F3
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #D4D8DA;
stroke-width: 2px;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: #6F257F;
stroke-width: 5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: #F1F3F3;
stroke: #6F257F;
stroke-width: 5px;
}
.hover-line {
stroke: #6F257F;
stroke-width: 2px;
stroke-dasharray: 3,3;
}
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<svg width="400" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
function make_graph(data) {
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var parseTime = d3.timeParse("%Y")
bisectDate = d3.bisector(function(d) {
return d.year;
}).left;
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var line = d3.line().x(function(d) {
return x(d.year);
}).y(function(d) {
return y(d.value);
});
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
data.forEach(function(d) {
d.year = parseTime(d.year);
d.value = +d.value;
});
x.domain(d3.extent(data, function(d) {
return d.year;
}));
y.domain([d3.min(data, function(d) {
return d.value;
}) / 1.005, d3.max(data, function(d) {
return d.value;
}) * 1.005]);
g.append("g")
.attr("class", "axis axis--x")
.style('font-family', 'Impact')
.style("fill", "green")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
d3.selectAll('g.axis--x text')
.style('fill', 'green')
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(6)
.tickFormat(function(d) {
return parseInt(d / 1000) + "k";
}))
.append("text")
.attr("class", "axis-title")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Population)");
g.append("path").datum(data)
.attr("class", "line")
.attr("d", line);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.attr("class", "x-hover-line hover-line")
.attr("y1", 0)
.attr("y2", height);
focus.append("line")
.attr("class", "y-hover-line hover-line")
.attr("x1", width)
.attr("x2", width);
focus.append("circle")
.attr("r", 7.5);
focus.append("text")
.attr("x", 15)
.attr("dy", ".31em");
svg.append("rect")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "overlay").attr("width", width)
.attr("height", height)
.on("moseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
focus
.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
focus.select("text").text(function() {
return d.value;
});
focus.select(".x-hover-line").attr("y2", height - y(d.value));
focus.select(".y-hover-line").attr("x2", width + width);
}
}
</script>
I made a small change in your code.
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.selectAll('text')
.style("fill", "yellow")
Here is the jsFiddle
Hope it helps :)
Related
Currently, this d3 js bar chart animates from top to bottom. Most likely its because of the way d3js renders its chart which starts from the top. This might be a common issue and might be easy for those who are familiar with the nuisance of d3js, how can I make this animate from bottom to top?
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
</style>
<svg width="1260" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.4),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("MRT_MonthlyAve_2014.csv", function(d) {
d.MonthlyAverage = +d.MonthlyAverage;
return d;
}, function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Station; }));
y.domain([0, d3.max(data, function(d) { return d.MonthlyAverage; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(12, "s"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.Station); })
.attr("y", function(d) { return y(d.MonthlyAverage); })
.transition().duration(1000)
.ease(d3.easeExp)
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.MonthlyAverage); });
});
</script>
MRT_MonthlyAve_2014.csv
Station,MonthlyAverage
North Avenue,2227081
Quezon Avenue,1018121
GMA,606110
Cubao,1410788
Santolan,260737
Ortigas,561910
Shaw Boulevard,1339020
Boni,631115
Guadalupe,1002740
Buendia,421302
Ayala,1145004
Magallanes,933713
Taft Avenue,2427220
Just set the initial y position to height:
.attr("y", height)
Here is your code with that change only (I'm reducing the height of the SVG, so you can see it working in the small snippet view):
var csv = `Station,MonthlyAverage
North Avenue,2227081
Quezon Avenue,1018121
GMA,606110
Cubao,1410788
Santolan,260737
Ortigas,561910
Shaw Boulevard,1339020
Boni,631115
Guadalupe,1002740
Buendia,421302
Ayala,1145004
Magallanes,933713
Taft Avenue,2427220`;
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.4),
y = d3.scaleLinear().rangeRound([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = d3.csvParse(csv, function(d) {
d.MonthlyAverage = +d.MonthlyAverage;
return d;
})
x.domain(data.map(function(d) {
return d.Station;
}));
y.domain([0, d3.max(data, function(d) {
return d.MonthlyAverage;
})]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(12, "s"))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.Station);
})
.attr("y", height)
.transition().duration(1000)
.ease(d3.easeExp)
.attr("y", function(d) {
return y(d.MonthlyAverage);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.MonthlyAverage);
});
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis--x path {
display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="1260" height="200"></svg>
I'd like to create a dot chart in d3.js, where it looks kind of like a bar chart, but there are stacked dots instead of bars.
This question is similar to what I want: D3.js (Wilkinson type) Dot Plot Example
I've used that code to try and accomplish what I want, but I'm stuck.
Here is an image showing what I want, based on the data for apples in my CSV file:
The code I have so far is below. When I console.log d3.range(d.apples), I see that I have an array for each day, which is what I want. However I can't figure out how to draw a circle for each spot in the array, where the x axis value would be the day # and the y axis value would be the position in the array + 1.
Any help very welcome! Thank you.
Code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
d3.csv("fruit.csv", function(error, data) {
if (error) throw error;
// console.log("data: ", data);
data.forEach(function(d) {
d.day = +d.day;
d.apples = +d.apples;
});
x.domain([0,4]);
y.domain([0,6]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount")
chart.selectAll("g")
.data(data)
.enter()
.append("g")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d, i) { return x(d.day); })
//.attr("cy", function(d,i){ return y(d.apples); })
.attr("cy", function(d,i,j){
var range = d3.range(d.apples);
console.log("apple range: ", range);
return y(range[i])
})
.style("fill", "blue")
.style("opacity", .5);
});
</script>
Data (fruit.csv):
"day","apples"
1,3
2,6
3,1
4,2
There are several ways to do what you want. I believe the most traditional approach is nesting the data and appending several groups, one for each fruit. But, for the sake of simplicity, I'd like to propose a different approach: let's change your data, flattening it, so, in each object, we have only one fruit:
originalData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
Where originalData is the data array the way it is right now.
Doing that, your data will become something like this:
[{
"day": "1",
"fruit": "apples",
"amount": 3
}, {
"day": "1",
"fruit": "pears",
"amount": 6
}, {
"day": "1",
"fruit": "oranges",
"amount": 3
}, {
"day": "2",
"fruit": "apples",
"amount": 6
},//etc...
]
Then, we set a ordinal scale for the days:
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
.domain(data.map(d => d.day));
And, finally, we paint the circles:
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5);
Here is a demo (I put some titles, so you can hover over the dot to check the fruit and the amount):
var rawData = d3.csv.parse(d3.select("#csv").text());
var data = [];
rawData.forEach(function(d) {
data.push({
day: d.day,
fruit: "apples",
amount: +d.apples
});
data.push({
day: d.day,
fruit: "pears",
amount: +d.pears
});
data.push({
day: d.day,
fruit: "oranges",
amount: +d.oranges
});
});
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10()
.domain(["apples", "pears", "oranges"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 10]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var dots = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.day);
})
.attr("cy", function(d) {
return y(d.amount)
})
.style("fill", function(d) {
return color(d.fruit)
})
.style("opacity", .5)
.append("title")
.text(function(d){ return d.fruit + ": " + d.amount});
pre {
display: none;
}
body {
font: 10 px sans - serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples","pears","oranges"
1,3,6,3
2,6,8,2
3,1,0,7
4,2,3,8</pre>
EDIT: After further clarifications in the comments, this is what OP wants:
Append the groups as you did, but translating them according to the day:
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
And, in the dots, set the data using d3.range:
var dots = groups.selectAll("circle")
.data(function(d){ return d3.range(+d.apples + 1)})
That way, for each group, the data for the circles goes all the way up to the maximum. For example, if in a given day "apples" is 3, the data will be:
[0, 1, 2, 3]
Or, if "apples" is 6, the data will be:
[0, 1, 2, 3, 4, 5, 6]
If you don't want the leading zero, just do d3.range(1, +d.apples + 1).
Here is a demo:
var data = d3.csv.parse(d3.select("#csv").text());
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 360 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangePoints([0, width], 0.5);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
var chart = svg.append("g")
.attr("id", "chart");
x.domain(data.map(d => d.day));
y.domain([0, 7]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Day");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("amount");
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.day) + ".0)";
});
var dots = groups.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.apples + 1)
})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cy", function(d) {
return y(d)
})
.style("fill", "blue")
.style("opacity", .5);
pre {
display: none;
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #999;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples"
1,3
2,6
3,1
4,2</pre>
I have created a D3 visualization which contains both a scatterplot and a line chart. They share an x-axis, but each have their own y-axis. My problem involves how to properly implement a brush and update both y-axes.
As you can see here the y-axes are correct initially and also correct again once the brush is turned off. However during 'brushing' both y-axes are set to the 'left' one. I see why this is happening when I setup my brush here:
brush = d3.svg.brush()
.x(brushFilterXScale)
.y(brushFilterTransactionsYScale)
.on('brush', brushed);
I also have a brushFilterBalanceYScale which is the scale for the axis on the right. My question is how do I pass BOTH of these scales to brush so that I can update each y-axis properly?
I don't know of a straight-forward way to do this. You can, however, reverse map it in your brush event:
function brushed() {
var extent = brush.extent(), //<-- the extent
yDomain = [extent[0][1], extent[1][1]]; //<-- the y domain of the extent
y2.domain(
[
y2Brush.invert(yBrush(yDomain[0])), //<-- take the yDomain start, get it's pixel position, then invert that back into the domain of the y2Brush
y2Brush.invert(yBrush(yDomain[1]))
]
);
Here's a working example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.line {
fill: none;
stroke: steelblue;
clip-path: url(#clip);
}
circle {
clip-path: url(#clip);
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 10,
right: 40,
bottom: 100,
left: 40
},
margin2 = {
top: 430,
right: 40,
bottom: 20,
left: 40
},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var x = d3.scale.linear().range([0, width])
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height, 0]),
xBrush = d3.scale.linear().range([0,width]),
yBrush = d3.scale.linear().range([height2, 0]),
y2Brush = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left"),
yAxis2 = d3.svg.axis().scale(y2).orient("right"),
yAxisBrush = d3.svg.axis().scale(yBrush).orient("bottom");
xAxisBrush = d3.svg.axis().scale(xBrush).orient("bottom");
var brush = d3.svg.brush()
.x(xBrush)
.y(yBrush)
.on("brush", brushed);
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return y(d.y);
});
var lineBrush = d3.svg.line()
.interpolate("monotone")
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return yBrush(d.y);
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var data1 = [],
data2 = [];
for (var i = 0; i < 100; i++) {
data1.push({
x: i,
y: Math.random() * 10
});
if (i % 3 === 0){
data2.push({
x: i,
y: Math.random() * 100
});
}
}
x.domain([0,100]);
y.domain([0, d3.max(data1.map(function(d) {
return d.y;
}))]);
y2.domain([0, d3.max(data2.map(function(d) {
return d.y;
}))]);
xBrush.domain(x.domain());
yBrush.domain(y.domain());
y2Brush.domain(y2.domain());
focus.append("path")
.datum(data1)
.attr("class", "line")
.attr("d", line);
var scatter = focus.append("g")
.selectAll("circle")
.data(data2);
scatter
.enter()
.append("circle")
.attr("cx", function(d){
return x(d.x);
})
.attr("cy", function(d){
return y2(d.y);
})
.attr("r", function(d){
d.r = Math.random() * 20;
return d.r;
})
.style("fill", "orange")
.style("opacity", "0.5");
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
focus.append("g")
.attr("transform", "translate(" + width + " ,0)")
.attr("class", "y2 axis")
.call(yAxis2);
context.append("path")
.datum(data1)
.attr("class", "line")
.attr("d", lineBrush);
context.append("g")
.selectAll("circle")
.data(data2)
.enter()
.append("circle")
.attr("cx", function(d){
return x(d.x);
})
.attr("cy", function(d){
return y2Brush(d.y);
})
.attr("r", function(d){
return d.r * 0.25;
})
.style("fill", "orange")
.style("opacity", "0.5");
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxisBrush);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
function brushed() {
var extent = brush.extent(),
yDomain = [extent[0][1], extent[1][1]];
y2.domain([y2Brush.invert(yBrush(yDomain[0])), y2Brush.invert(yBrush(yDomain[1]))]);
x.domain(brush.empty() ? xBrush.domain() : [extent[0][0], extent[1][0]]);
y.domain(brush.empty() ? yBrush.domain() : yDomain);
scatter
.attr("cx", function(d){
return x(d.x);
})
.attr("cy", function(d){
return y2(d.y);
})
focus.select(".line").attr("d", line);
focus.select(".x.axis").call(xAxis);
focus.select(".y.axis").call(yAxis);
focus.select(".y2.axis").call(yAxis2);
}
</script>
The problem:
I'm new to D3.js and I've created a bar chart that gets data from an array. I placed text on top of the bars and the text is wider than the bars themselves. I want to rotate it vertical so that the text goes through the middle of the bar.
I know .attr("transform", "rotate(180)") won't work because it rotates my text off-screen. I found an answer from Mike Bostock here, but it doesn't seem to work for me, I don't know what I'm doing wrong. He says I need to change the rotation point of origin, but I can't get it to work.
Any help is appreciated, you can find the code below:
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d){
return d;
})
.attr("x", function(d, i){
xText = i*(w/data.length);
return xText;
})
.attr("y", function(d){
yText = h - yScale(d);
return yText;
})
.attr("transform", "translate(" + xText + "," + yText + ") rotate(90)")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
Thanks!
You are setting both the x and y attributes and translated the text. With the rotate, you only want to use the translate:
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
Also, I find your example code confusing, I was assuming that your data was an array of strings since you do:
.text(function(d) {
return d;
})
but that would make yScale(d), return NaN.
Here's an example that works nicely:
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var w = 300,
h = 300,
data = [{
t: "Now",
v: 50
},{
t: "is",
v: 25
},{
t: "the",
v: 10
},{
t: "winter",
v: 30
}];
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
var yScale = d3.scale.linear()
.range([0, h])
.domain([60, 0]);
var label = svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.t;
})
.attr("transform", function(d,i){
var xText = i * (w / data.length);
var yText = h - yScale(d.v);
return "translate(" + xText + "," + yText + ") rotate(90)";
})
.attr("fill", "black")
.attr("font-family", "sans-serif")
.attr("font-size", "11px");
</script>
</body>
</html>
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var data = [{
"letter": "A",
"frequency": 0.08167
}, {
"letter": "B",
"frequency": 0.01492
}, {
"letter": "C",
"frequency": 0.02782
}, {
"letter": "D",
"frequency": 0.04253
}, {
"letter": "E",
"frequency": 0.12702
}];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "%");
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 + ")");
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.letter);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
function type(d) {
d.frequency = +d.frequency;
return d;
}
var text = svg.selectAll(".text").data(data);
text.enter()
.append("text")
.attr("class", "text")
.attr("x", function(d, i) {
return x(d.letter);
})
.attr("y", function(d) {
return y(d.frequency);
})
.text(function(d) {
return d.letter;
})
.attr("transform", function(d) {
return "rotate(90 " + x(d.letter) + "," + (y(d.frequency) - d.letter.length) + ")"
});
</script>
You can try out this, and as a base you can modify this as per your need. I hope it helps.
I am trying to draw bubbles on bar charts. On every bar there will be a bubble.That Bubbles will be generate from a different json file. Can you please provide me any suggestion. Thanks in advance.
Code below.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: #ccc;
}
.x.axis path {
display: none;
}
.node {
fill: #000;
z-index: 999;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
var svg1 = 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.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.frequency; })]);
svg1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg1.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.frequency); })
.attr("height", function(d) { return height - y(d.frequency); })
.append("bubble")
.attr("class","bub");
var diameter = 30,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select(".bub").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("flare.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.packageName); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); });
});
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");});
function type(d) {
d.frequency = +d.frequency;
return d;
}
</script>
//File for bar chart is below
data.tsv
letter frequency
A .08167
B .01492
C .02780
D .04253
//Json for bubble charts
flare.json
{
"name": "flare",
"children": [
{"name": "Swaraj", "size": 20}
]
}
I would like to display the bubble chart on bar graph.