Label at the beginning of a D3 axis - d3.js

How would I make d3 put "Tue 20" to the left of "Wed 21"?
http://jsfiddle.net/robdodson/KWRxW/
var data = [
{"date":"2012-03-20","total":3},
{"date":"2012-03-21","total":8},
{"date":"2012-03-22","total":2},
{"date":"2012-03-23","total":10},
{"date":"2012-03-24","total":3},
{"date":"2012-03-25","total":20},
{"date":"2012-03-26","total":12}
];
var margin = {top: 40, right: 40, bottom: 40, left:40},
width = 600,
height = 500;
var x = d3.time.scale()
.domain([
new Date(data[0].date),
d3.time.day.offset(new Date(data[data.length - 1].date), 1)
])
.rangeRound([0, width - margin.left - margin.right]);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.total; })])
.range([height - margin.top - margin.bottom, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom')
.ticks(d3.time.days, 1)
.tickFormat(d3.time.format('%a %d'))
.tickSize(0)
.tickPadding(8);
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.tickPadding(8);
var svg = d3.select('body').append('svg')
.attr('class', 'chart')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
svg.selectAll('.chart')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function(d) { return x(new Date(d.date)); })
.attr('y', function(d) {
return height - margin.top - margin.bottom -
(height - margin.top - margin.bottom - y(d.total))
})
.attr('width', 10)
.attr('height', function(d) {
return height - margin.top - margin.bottom - y(d.total)
});
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + (height - margin.top - margin.bottom) + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);

It happened due to inconsistencies in date managing. By using UTC dates and the d3 date functions the issue will not occur.
https://github.com/mbostock/d3/issues/1234

Related

tickSizeOuter not equal zero, but just first and last tick of yAxis shows line

I have a line chart. And just the first and last tick of yAxis shows the outer line.
The result as below image:
The expected result is that every tick of yAxis shows outer line.
Example:
https://bl.ocks.org/d3noob/c506ac45617cf9ed39337f99f8511218
And here is my reproduce code:
var margin = { top: 70, right: 30, bottom: 50, left: 40 }
var width = 720 - margin.left - margin.right
var height = 430 - margin.top - margin.bottom
var data = [
{angle:0.6933744221879815,x:0.1},
{angle:0.6933744221879815,x:0.1},
{angle:0.6933744221879815,x:0.3},
{angle:0.6933744221879815,x:0.4},
{angle:0.6933744221879815,x:0.4},
{angle:0.6933744221879815,x:0.5},
{angle:0.6933744221879815,x:0.7},
{angle:0.6933744221879815,x:0.8},
{angle:0.6933744221879815,x:0.8},
{angle:0.6933744221879815,x:0.9},
{angle:0.6933744221879815,x:1},
{angle:0.6933744221879815,x:1.1},
{angle:0.6933744221879815,x:1.2},
{angle:0.6933744221879815,x:1.3},
{angle:0.6933744221879815,x:1.4},
{angle:0.6933744221879815,x:1.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 x = d3.scaleLinear()
.range([0, width])
.domain([data[0].x , data[data.length - 1].x])
var y = d3.scaleLinear()
.range([height, 0])
.domain([-30, 120])
var xAxis = d3.axisBottom()
.scale(x)
.tickValues(x.ticks(8).concat(x.domain()[0]))
.tickFormat(d => d +'s')
.tickPadding(8)
.tickSizeOuter(0)
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8)
.tickSizeInner(-width)
.tickSizeOuter(4)
.tickPadding(8);
var line = d3.line()
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.angle);
})
.curve(d3.curveCatmullRom);
svg.append('path')
.attr('d', line(data))
.attr('stroke', '#ff5722')
.attr('stroke-width', '2')
.attr('fill', 'none');
svg.append('g')
.call(xAxis)
.attr('transform', `translate(0, ${height})`)
svg.append('g')
.call(yAxis)
.tick line{
opacity: 0.2;
}
.tick text{
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
There is no missing tick, all those inner ticks are there! However, they are going to the right hand side of the chart, because you did this:
yAxis.tickSizeInner(-width)
In other words, your light grey gridlines are your ticks.
If you want to keep those light grey gridlines and draw the regular ticks, you have to do a different approach. Like this:
svg.append('g')
.call(yAxis)
.selectAll(".tick")
.append("line")
.attr("x2", width)
.style("stroke", "#eee")
Here is your code with that change:
var margin = { top: 70, right: 30, bottom: 50, left: 40 }
var width = 720 - margin.left - margin.right
var height = 430 - margin.top - margin.bottom
var data = [
{angle:0.6933744221879815,x:0.1},
{angle:0.6933744221879815,x:0.1},
{angle:0.6933744221879815,x:0.3},
{angle:0.6933744221879815,x:0.4},
{angle:0.6933744221879815,x:0.4},
{angle:0.6933744221879815,x:0.5},
{angle:0.6933744221879815,x:0.7},
{angle:0.6933744221879815,x:0.8},
{angle:0.6933744221879815,x:0.8},
{angle:0.6933744221879815,x:0.9},
{angle:0.6933744221879815,x:1},
{angle:0.6933744221879815,x:1.1},
{angle:0.6933744221879815,x:1.2},
{angle:0.6933744221879815,x:1.3},
{angle:0.6933744221879815,x:1.4},
{angle:0.6933744221879815,x:1.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 x = d3.scaleLinear()
.range([0, width])
.domain([data[0].x , data[data.length - 1].x])
var y = d3.scaleLinear()
.range([height, 0])
.domain([-30, 120])
var xAxis = d3.axisBottom()
.scale(x)
.tickValues(x.ticks(8).concat(x.domain()[0]))
.tickFormat(d => d +'s')
.tickPadding(8)
.tickSizeOuter(0)
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8)
.tickSizeOuter(4)
.tickPadding(8);
var line = d3.line()
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.angle);
})
.curve(d3.curveCatmullRom);
svg.append('path')
.attr('d', line(data))
.attr('stroke', '#ff5722')
.attr('stroke-width', '2')
.attr('fill', 'none');
svg.append('g')
.call(xAxis)
.attr('transform', `translate(0, ${height})`)
svg.append('g')
.call(yAxis)
.selectAll(".tick")
.append("line")
.attr("x2", width)
.style("stroke", "#eee")
.tick text{
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Unable to create a line chart in D3.js

I want to create a line chart using D3.js.
Here an example of line chart.
This is my code:
var margin = {top: 0, right: 0, bottom: 0, left: 0};
var svg = d3.select('#linechart')
.append('svg')
.attr('width', 600)
.attr('height', 200);
var values = createAxisLine(svg);
var x = values[0];
var y = values[1];
var width = values[2];
var height = values[3];
createChartLine(svg, x, y, width, height);
function createAxisLine(thisSvg) {
var width = thisSvg.attr('width') - margin.left - margin.right;
var height = thisSvg.attr('height') - margin.top - margin.bottom;
thisSvg = thisSvg.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
var x = d3.scaleBand()
.rangeRound([0, width])
.domain([2016, 2015, 2014, 2013, 2012, 2011, 2010]);
var y = d3.scaleLinear()
.range([height, 0])
.domain([0, 100]);
var xAxis = d3.axisBottom(x).tickSize(0, 0);
var yAxis = d3.axisLeft(y);
thisSvg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
thisSvg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + margin.left + ', 0)')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
return [x, y, width, height];
}
function createChartLine(thisSvg, x, y, width, height) {
thisSvg.selectAll(null)
.data(mydata)
.attr('transform', function(d) {
return 'translate(' + margin.left + ', ' + margin.top + ')';
});
var line = d3.line()
.x(function(d) {
return x(d.year);
})
.y(function(d) {
if(isNaN(d.value)) return 0;
else return y(d.value);
});
lines.append('path')
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.5)
.attr('d', line);
}
All the code is in this plunker.
When I run the code, nothing appears, no lines or axis are showed. But data are correctly getted so I don't understand what the problem is.
I get this error:
I need help
The problem for why nothing appears is that your the code inside script.js runs before the linechart element is loaded. One of the recommendation would be to include your script.js before the close of your body tag.
<body>
<div id='linechart'></div>
<script src="script.js"></script>
</body>

D3 Bar Chart, bars not showing, axis on top

I have a codepen here - https://codepen.io/ttmtsimon/pen/EbPgrw
I am trying to create a simple bar chart in D3.
The axis are showing but the x axis is on the top and it should be on the bottom and the bars are not showing at all.
I know this is a bad question but I can't see where the code is wrong, any help would be appreciated.
let margin = {top: 20, right: 20, bottom:100, left:60}
let width = 800 - margin.left - margin.right
let height = 500 - margin.top - margin.bottom
let x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.5);
let y = d3.scaleLinear().range([height, 0])
let xAxis = d3.axisBottom(x)
.tickFormat((d) => {
return d.x;
});
let yAxis = d3.axisLeft(y)
let svg = d3.select('.barGraph')
.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.json('https://api.myjson.com/bins/vkbjf', (data)=>{
x.domain(data.map((d)=>{
return d.name
}))
y.domain([0, d3.max(data, (d)=>{
return d.rank
})]);
svg.append('g')
.attr('class', 'x axis')
.attr('tranform', 'translate(0 " + height + ")')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-0.5em')
.attr('dy', '-55em')
.attr('y', 30)
.attr('transform', 'rotate(-45)');
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 5)
.attr('dy', '0.8em')
.attr('text-anchor', 'end')
.text('Member Rank')
svg.selectAll('bar')
.data(data)
.enter()
.append('rect')
.style('fill', 'orange')
.attr('x', (d)=>{
return y(d.rank)
})
.attr('height', (d)=>{
return height - y(d.rank)
})
})
There are a few issues, namely, you don't define width or y for your rectangles. Also, the rectangle height should be equal to y(value) rather than height-y(value) (this is the top of the rectangle, and therefore the rectangle's y property, as svg coordinates start from the top at y=0). Your rectangle attributes should probably look more like:
svg.selectAll('bar')
.data(data)
.enter()
.append('rect')
.style('fill', 'orange')
.attr('x', (d)=>{
return x(d.name)
})
.attr('height', (d)=>{
return y(d.rank)
})
.attr("width",x.bandwidth())
.attr("y",(d)=>height - y(d.rank))
})
Second, you have inconsistent quotations in your transforms (svg and x axis appends respectively below):
.attr('transform', 'translate(" + margin.left + "." + margin.top + ")');
.attr('tranform', 'translate(0 " + height + ")')
Which is why these aren't being appended right, also you need commas between the two values.
Updated pen.

D3 axes render on top of graph

The following code renders the axes on top of the graph and I can't seem to find where to add/subtract pixels to align the two.
I've spent weekend trying to solve this but I feel stuck. In my desperation, I've tried to add and subtract the padding in various places, add margins here and there to move things. It's like the graph and the axes are on two different scales but I can't see where I'm doing that either. This is a link to my codepen: http://codepen.io/piacoding/pen/amzoog?editors=0010
thank you,
var w = 780;
var h = 500;
var padding = 60;
var svg = d3.select("#graph")
.append("svg:svg")
.attr("width", w)
.attr("height", h )
.attr('class', 'gdp');
// define the x scale (horizontal)
var mindate = new Date(1947, 0, 1),
maxdate = new Date(2015, 6, 1);
var xScale = d3.time.scale()
.domain([mindate, maxdate])
.range([padding, w - padding]);
var maxnumber = d3.max(dataset, function(d) {
return d[1]
});
var yScale = d3.scale.linear()
.domain([0, maxnumber])
.range([0, h]);
var y = d3.scale.linear()
.domain([0, maxnumber])
.range([h - padding, padding]);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (yScale(d[1]));
})
.attr("width", w / dataset.length)
.attr("height", function(d) {
return yScale(d[1]);
})
// define the y axis
var yAxis = d3.svg.axis()
.orient("left")
.scale(y);
// define the y axis
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale);
// draw y axis
svg.append("g")
.attr("transform", "translate(" + padding + ",0)")
.attr('class', 'y axis')
.call(yAxis);
// draw x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
Take a look at Margin Convention which does exactly what you need. See http://codepen.io/anon/pen/JRovxV?editors=0010 for the updated version:
var margin = {top: 20, right: 10, bottom: 60, left: 60};
var width = 780 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("#graph")
.append("svg:svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('class', 'gdp');
// define the x scale (horizontal)
var mindate = new Date(1947, 0, 1),
maxdate = new Date(2015, 6, 1);
// var firstDate = dataset[0];
// var lastDate = dataset[dataset.length - 1][0];
var xScale = d3.time.scale()
.domain([mindate, maxdate])
.range([0, width]);
var maxnumber = d3.max(dataset, function(d) {
return d[1]
});
var yScale = d3.scale.linear()
.domain([0, maxnumber])
.range([0, height]);
var y = d3.scale.linear()
.domain([0, maxnumber])
.range([height, 0]);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (width / dataset.length);
})
.attr("y", function(d) {
return height - (yScale(d[1]));
})
.attr("width", width / dataset.length)
.attr("height", function(d) {
return yScale(d[1]);
})
// define the y axis
var yAxis = d3.svg.axis()
.orient("left")
.scale(y);
// define the y axis
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale);
// draw y axis
svg.append("g")
//.attr("transform", "translate(" + padding + ",0)")
.attr('class', 'y axis')
.call(yAxis);
// draw x axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);

d3 bar chart axis alignment

I am trying to use the margin conventions described in http://bl.ocks.org/mbostock/3019563
when plotting bar charts. However, the bars do not align with the x-axis as you can see
in this basic example: http://bl.ocks.org/kyrre/bbd29f225173825797e3. What am I doing wrong?
var data = [
{x: "Differential Geometry", y: 10},
{x: "Statistical Physics", y: 5},
{x: "Music", y: 3}
]
var margin = {top: 20, right: 10, bottom: 20, left: 50};
var width = 500 - margin.left - margin.right;
var height = 320 - margin.top - margin.bottom;
var y = d3.scale.linear()
.domain([0, d3.max(data, function(x) {
return x.y;
})])
.range([0, height]);
var x = d3.scale.ordinal()
.domain(_.map(data, function(d) { return d.x;}))
.rangeRoundBands([0, width], 0.10);
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 rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", 0)
.attr("height", function(d) {
return y(d.y);
})
.attr("fill", function(d) { return "blue";})
.attr("width", 20);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
The y coordinates are counted from the top (i.e. 0 is at the top of the image). It should work if you set y to the total minus height.
.attr("y", function(d) { return (height - y(d.y)); })

Resources