I'm graphing data with long Y-axis labels (temperature ranges). How do I adjust the labels so that they're centered on the ticks when they're rotated 90 degrees and don't run off the edge of the graph? I've attempted to use this.getBBox() to apply a transform based on the iteration of the label, but that doesn't seem to work.
Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var categories = {
temperature: ["29-30°C - Red","31-33° C - Green","> 33° C - Blue"],
sleep: ["7 - 8 hours", "5 - 6 hours", "4 or less hours"]
}
var mindate = new Date(2012,0,1),
maxdate = new Date(2012,0,31);
var xScale = d3.time.scale()
.domain([mindate, maxdate]) // values between for month of january
.range([0, width - margin.right]);
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale);
var yKey = "temperature";
var yScale = d3.scale.ordinal()
.domain(categories[yKey])
.rangePoints([0, height]);
var yAxis = d3.svg.axis()
.scale(yScale)
.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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "yAxis axis")
.call(yAxis)
.selectAll("text")
.attr("x", 10)
.attr("y",20)
.attr("transform", "rotate(90)")
.style("text-anchor", "start")
</script>
Here's what I've tried but this doesn't seem to move the labels much. I'm not sure how to set the "x" inside the following function without using a translate.
d3.selectAll(".yAxis text")
.attr("transform", function(d,i) {
return "translate(" + -20 + "," + ((this.getBBox().width/(i+2))) + ")rotate(-90)"
})
Aha! The answer is that d3.scale.ordinal has a padding setting, so all I have to do is change my yScale to
var yScale = d3.scale.ordinal()
.domain(categories[yKey])
.rangePoints([0, height],1);
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var categories = {
temperature: ["29-30°C - Red","31-33° C - Green","> 33° C - Blue"],
sleep: ["7 - 8 hours", "5 - 6 hours", "4 or less hours"]
}
var mindate = new Date(2012,0,1),
maxdate = new Date(2012,0,31);
var xScale = d3.time.scale()
.domain([mindate, maxdate]) // values between for month of january
.range([0, width - margin.right]);
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale);
var yKey = "temperature";
var yScale = d3.scale.ordinal()
.domain(categories[yKey])
.rangePoints([0, height],1);
var yAxis = d3.svg.axis()
.scale(yScale)
.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 + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "yAxis axis")
.call(yAxis)
.selectAll("text")
.attr("x", 10)
.attr("y",20)
.attr("transform", "rotate(90)")
.style("text-anchor", "middle")
</script>
Related
I want to add custom tick labels on the x axis,like 1,2,3,4,3,2,1 in this pattern. But the code that I am using doesn't show the decreasing numbers.
var margin = {
top: 100,
right: 100,
bottom: 100,
left: 100
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain([1, 2, 3, 4, 5, 4, 3, 2, 1])
.rangePoints([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("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 + ")");
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
Any kind of help would be appreciated. Thanks.
Little hack it needed to solve this issue.
Cause
d3.scale.ordinal(), domain calculation in purely mathematical logic. Each
domain associated with a range. ( f(x)=y ).
Duplication not allowed because it will make ambiguity
Solution
Create linear scale with total item in the axis as domain [0, itemLength]
While creating axis use tickFormat to find out the index of the element
var margin = {
top: 100,
right: 100,
bottom: 100,
left: 100
},
width = 560 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xdata = ["1", "2", "3", "4", "5", "4", "3", "2", "1", "0"];
var linear = d3.scale.linear()
.domain([0, 10])
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(linear)
.orient("bottom");
var axis = d3.svg.axis()
.scale(linear)
.orient("top")
.ticks(10)
.tickFormat(function (d) {
return xdata[d];
});
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 + ")");
svg.append("g")
.attr("class", "x axis")
.call(axis);
.axis path, line {
fill: none;
stroke: #000;
stroke-linecap: round;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have two questions. I am trying to plot a function with d3.js. I have already come far, but I have two problems.
My code currently; https://jsfiddle.net/v0du66ey/
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 50, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
padding = 50;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
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 data = [];
for (var k = 0; k < 100; k++) {
data.push({x: k, y: k});
}
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
// Define x domain
x.domain([-10, 10]);
// Define y domain
y.domain([-10, 10]);
// Add x axis
svg.append("g")
.attr("class","x axis")
.attr("transform","translate(0," + height + ")")
.call(xAxis)
// Add y axis
svg.append("g")
.attr("class","y axis")
.call(yAxis);
// Add x grid
svg.append("g")
.attr("class","grid")
.attr("transform","translate(0," + height + ")")
.call(xAxis
.tickSize(-height,-height,0)
.tickFormat("")
);
// Add y grid
svg.append("g")
.attr("class","grid")
.call(yAxis
.tickSize(-width,-width,0)
.tickFormat("")
);
// Add x axis label
svg.append("text")
.attr("transform", "translate(" + (width / 2) + "," + (height + margin.bottom) + ")")
.style("font-size","15")
.style("text-anchor", "middle")
.text("x axis");
// Add y axis label
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y",0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("font-size","15")
.style("text-anchor", "middle")
.text("y axis");
svg.append("path")
.attr("class", "line")
.attr("d",line(data));
</script>
As you can see the function which I have defined, y = x. Is not correctly plotted along its entire domain. It goes for x from -10 to approx 2.5 and y from 10 to approx 7.5. What goes wrong here? I do not namely see it.
Furthermore I have a cosmetic question. I am trying to create a border around the whole plot on the axis and trying to get the ticks bars to point to the inside of the graph instead of outside. In other words, I want to have it more looking like, a Matlab plot,
About cosmetic question: you need to set innerTickSize(), outerTickSize() and tickPadding() on your axises with a negative numbers.
About main question:
as far as you have scales you should set line's x and y data as a result of scale functions.
so your line code will look like this:
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); })
.interpolate("linear");
Here is jsfiddle with updated sample http://jsfiddle.net/n3Lndkum/
I have a this script which try to plot the x and y-axis:
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(["apple", "orange", "banana"])
.rangePoints([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("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 + ")");
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
var y = d3.scale.ordinal()
.domain(["1", "2", "3"])
.rangePoints([0, 3]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
As you can see upon running the code the y-axis is squeezed down. How can I unsqueezed it?
You need to set your y range correctly
var y = d3.scale.ordinal()
.domain(["1", "2", "3"])
.rangePoints([height, 0]);
From the documentation (https://github.com/mbostock/d3/wiki/Ordinal-Scales#ordinal_rangePoints),
# ordinal.rangePoints(interval[, padding])
Sets the output range from the specified continuous interval. The
array interval contains two elements representing the minimum and
maximum numeric value. This interval is subdivided into n
evenly-spaced points, where n is the number of (unique) values in the
input domain.
You input domain has 3 values which you want to space out over the height of the chart.
Stack Snippet
You might also want to translate your x axis to the bottom (if not, just revert the translate on the x axis)
var margin = {top: 100, right: 100, bottom: 100, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(["apple", "orange", "banana"])
.rangePoints([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("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 + ")");
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.attr("transform", "translate(0, " + height + ")");;
var y = d3.scale.ordinal()
.domain(["1", "2", "3"])
.rangePoints([height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
I'm a beginner with d3 javascript and I don't know how to change the y axis on this grouped bar chart:
http://bl.ocks.org/mbostock/3887051 Data + code can be found here
This is the code of the site & the data of the grouped bar chart:
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
</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 x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis() //creating a generic axis function//
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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.csv("gender_ratio.csv", function(error, data) {
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "Perioden"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]*1000}; });
});
x0.domain(data.map(function(d) { return d.Perioden; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
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("Population");
var Perioden = svg.selectAll(".Perioden")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.Perioden) + ",0)"; });
Perioden.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.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", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
Data:
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
TX,2027307,3277946,1420518,2454721,7017731,5656528,2472223
NY,1208495,2141490,1058031,1999120,5355235,5120254,2607672
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
PA,737462,1345341,679201,1203944,3157759,3414001,1910571
If your x-axis is regular old numeric data, you should be using a linear scale not an ordinal. Ordinal is meant for discrete values (think a, b, c or x, y, z or tom, dick, harry) while linear is meant for continuous data (think 1,2,3 or 50, 100, 150):
var x = d3.scale.linear()
.range([0, width])
.domain([1650, 1700]);
In d3 speak, range is the pixel span of your data (from min to max), while domain is the user-space span of your data (the min and max of your data values). The scale that's returned then maps your user space data to it's pixel space position.
Below is a heavily commented example of a simple d3 bar graph:
<!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: steelblue;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
// sample data with x and y values
// d3 likes arrays of objects
var data = [
{
x: 1660,
y: 1
},{
x: 1670,
y: 2
},{
x: 1680,
y: 3
},{
x: 1690,
y: 4
}
];
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0,width]) // our pixel span
.domain([1650, 1700]); // our user space data span
var y = d3.scale.linear()
.range([height, 0]) // same thing as x, pixel span
.domain([0,5]); // user space space
// marry the scale to the axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
// set up our svg tag
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 + ")");
// draw x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// draw y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// get a nice bar width
// it is the width of our axis divided by the number of ticks
var barWidth = (width / xAxis.ticks()[0]);
// draw the bars
var state = svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class","bar")
.attr("width", barWidth)
.attr("x", function(d) { return x(d.x) - (barWidth / 2); }) // center it on tick
.attr("y", function(d) { return y(d.y); }) // y is the top of the bar
.attr("height", function(d) { return height - y(d.y); }); // and height goes to axis
</script>
</body>
</html>
With a multiple series line chart D3.js is it possible to manually specify the colour of each line?
data.csv
source_,track_index,alt,dist
series1,xxx,100,100
series1,xxx,200,200
series2,xxx,100,100
series2,xxx,200,200
.html file
<!DOCTYPE html>
<style>
body { font: 12px Arial;}
path {stroke: steelblue;stroke-width: 2;fill: none;}
.axis path,.axis line {fill: none;stroke: grey;stroke-width: 1;shape-rendering: crispEdges;}
.line {fill: none;stroke: steelblue;stroke-width: 1.5px;}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 30, left: 50},width = 1400 - margin.left - margin.right,height = 600 - margin.top - margin.bottom;
var parseDate = d3.time.format("%b %Y").parse; // Parse the date / time
var color = d3.scale.category10();
var x = d3.scale.linear().range([0,width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var alt_line = d3.svg.line()
.x(function(d) { return x(d.dist_); })
.y(function(d) { return y(d.alt_); });
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.csv("/data.csv", function(error, data) {
data.forEach(function(d) {
d.dist_ = d.dist;
d.alt_ = d.alt;
});
x.domain([0, 30]);
y.domain([0, 3000]);
var dataNest = d3.nest()
.key(function(d) {return d.source_;})
.entries(data);
dataNest.forEach(function(d) {
svg.append("path")
.attr("class", "line")
.attr("d", alt_line(d.values));
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>