I have a problem setting the x axis for a simple visualization made with D3.js.
I have something like the following image:
Obtained with this code (taken from an example I'm following):
var start_year = 2006, end_year = 2015;
var x = d3.scale.linear().range([0, width]);
var xAxis = d3.svg.axis().scale(x).orient("top");
var formatYears = d3.format("0000");
xAxis.tickFormat(formatYears);
var svg = d3.select("#page_content").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("margin-left", 0)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([start_year, end_year]);
var xScale = d3.scale.linear()
.domain([start_year, end_year])
.range([0, width]);
var g = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + 0 + ")")
.call(xAxis);
The problem is that I'm dealing with football seasons, so I would like to have for the x axis domain something like "2006/2007", "2007/2008" and so on.
I tried by creating an array of strings and using it as x domain:
domain_data = ["2006-2007","2007-2008","2008-2009","2009-2010","2010-2011","2011-2012","2012-2013","2013-2014","2014-2015","2015-2016"];
var x = d3.scale.ordinal().rangeRoundBands([0, width]);
var xAxis = d3.svg.axis().scale(x).orient("top");
var svg = d3.select("#page_content").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("margin-left", 0)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(domain_data.map(function(d) { return d; }));
var xScale = d3.scale.linear()
.domain([start_year, end_year])
.range([0, width]);
var g = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + 0 + ")")
.call(xAxis);
but, as you can see from the following image, each label is slightly shifted on the right or on the left with respect to the circles.
I'm very new to D3.js so if someone can explain me how to fix this problem, or maybe a better solution to achieve my goal, it'll be very helpful. Thanks in advance
** * EDIT: Added a JSFiddle * **
This is the JSFiddle you asked, hope it helps finding the solution. Thank you
Related
I'm trying to create a D3 line chart using data from an array. I am getting the data from the database but I can't seem to get the path lines to populate. I don't get any errors in the console so I'm a little confused on what I'm doing wrong. I'm still new to D3.
Here is my code:
//set dimensions
var margin = {top:30, right:20, bottom:30, left:50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
//parse date/time
var parseDate = d3.time.format("%Y-%b-%d").parse;
//set ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
//define axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
//define the line
var volumesLine = d3.svg.line()
.x(function(d) {return x(parseDate(d.MonthStartDate));})
.y(function(d) {return y(d.Volumes);});
//get data from database
var data = {MonthStartDate: [errorData[1]["MonthStartDate"]], Volumes: [errorData[1]["Volume"]], VolumeId: [errorData[1]["VolumeId"]]};
console.log(data);
//nest data by volume id
data = d3.nest()
.key(function(d){ return d.VolumeId; })
.entries(data);
data.forEach(function(d) {
svg.append("path")
.attr("class", "line")
.attr("d", volumesLine(d.values));
});
x.domain(d3.extent(data, function(d){ return d.MonthStartDate; }));
y.domain([0, d3.max(data, function(d){ return d.Volumes; })]);
//add the svg to canvas
var svg = d3.select("#volumeChart")
//.datum(data)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
//add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//create path
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", volumesLine);
I ma shifting the X-Axis to bottom, it is not visible and only coming when its on the bar chart. There is some svg area problem which I ma not able to find out. how to shift the barchart a bit upwards so that X=Axis labeling could be accommodated.
Here is the fiddle link Working but X-Axis Label is on the Top
a = 100;
b = 150;
c = 103;
dataset= [a,b,c];
var w = 500;
var h = 250;
var barPadding = 1;
var marginleft = 1;
var margintop =1;
var marginbottom =15;
margin = {top:1, right:10, bottom:1, left:1};
colors = ["#e41a1c", "#377eb8", "#4daf4a"];
h = h ;
var category= ['A', 'B', 'C'];
var x = d3.scale.ordinal()
.domain(category)
.rangeRoundBands([0, w]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(0);;
//Create SVG element
var svg = d3.select("#hello")
.append("svg")
.attr("width", w )
.attr("height", h )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// GENERATING RECTANGLES AND MAKING BAR CHART
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*1.5) ;
})
.attr("width", w / dataset.length - barPadding)
.attr("height", function(d) {
return (d*2 );
})
.attr("fill", function(d,i) {
return colors[i];
// .attr("fill", function(d) {
// return "rgb(0, 0, " + (d * 10) + ")";
});
var x_Axis = svg.append('g')
.attr('class','xnewaxis')
.attr("transform", "translate(0," + (20) + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "start")
.attr("dx", "-2.5em")
.attr("dy", ".5em")
.attr("transform", "rotate(-15)" );
Your code has several problems:
two different datasets for the bars;
lacks an ordinal scale for positioning the bars (actually, there is one, which you don't use);
lacks a linear scale for the bars values;
calls xAxis twice, with different translations;
But, for solving the axis problem, you just need to translate it correctly:
var x_Axis = svg.append('g')
.attr('class','xnewaxis')
.attr("transform", "translate(0," + (h- 30) + ")")
//30 here is the padding from the bottom of the SVG
Here is your fiddle: https://jsfiddle.net/gfwo0br9/
The bars are still showing up behind the axis (actually, the bars are going way below the end of the SVG itself). To fix that, you'll have to draw the bars properly (with a scale setting the range and the domains).
I am totally new to D3.js. I am trying to create a simple sparkline with time on the X axis and numbers upto 1000 on the Y axis. I have a web socket server which pushes random numbers unto 1000 to clients. I would like to plot a graph with these values on Y axis and time (in 24 hrs format) on the X axis. I tried a few things, but none of them work properly. My graph turns out vertical and is displayed only sometimes.
Appreciate any help in getting this working.
Below is what I have in my code, which is a tweaked version of an example I found online.
data[] is populated from the web socket call.
var margin = {top: 20, right: 20, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, data[data.length -1]])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, 100])
.range([height, 0]);
var line = d3.svg.line()
.x(function(d, i) {
var dt = new Date();
var time = dt.getHours() + ":" + dt.getMinutes() + ":" + dt.getSeconds();
return dt.getSeconds();
})
.y(function(d, i) { console.log("D is " + d+ "Y is " + y(d));return y(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("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0," + x(0)+")")
.call(d3.svg.axis().scale(y).orient("left"));
var path = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
redraw();
function redraw() {
path
.attr("d", line)
.attr("transform", null)
.transition()
.duration(500)
.ease("linear")
.attr("transform", "translate(" + x(-1) + ",0)")
.each("end", tick);
data.shift();
}
Could someone point me to a beginner's tutorial to D3 which will help me get started with creating different types of graphs?
It may help to have the structure of data you are using.
If you want to go for Angular+D3js there are a few libraries out ther that will help you get the job done quickly.
An simple library is:
http://angularjs-nvd3-directives.github.io/angularjs-nvd3-directives/
Another good set of directives which use D3.js is
http://krispo.github.io/angular-nvd3/#/
The Home page has a lot of examples you can check.
Although it lacks of documentation, every example has a plunker and you can check how it works.
I'm drawing a simple line chart with axes.
// Set the scales
var x = d3.scale.ordinal()
.domain(data.map(function(d) {
return d.labels;}))
.rangeRoundBands([0, width], 0);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.values; })])
.range([height, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
// draw line graph
var line = d3.svg.line()
.x(function(d) { return x(d.labels); })
.y(function(d) { return y(d.values); })
.interpolate("linear");
// Create the SVG 'canvas'
var svg = d3.select("body").append("svg")
.attr("class", "chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom).append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("path")
.attr("d",line(data))
.attr("stroke", color)
.attr("stroke-width", 2)
.attr("fill", "none");
Here you can see that it works, but the line starts from the y-axis so that the values don't correspond to the labels: http://jsfiddle.net/5r63j/12/
It may be fixed by replacing the .rangeRoundBands([0, width], 0); by .rangePoints([0, width], 0);, so that the values correspond to the labels and it's quite good, but there is no padding between the y-axis and the line.
Is it possible to move the start point of the line itself?
Close but not quite! :)
Instead of:
.rangePoints([0, width], 0);
you just use
.rangePoints([0, width], 0.5);
And you will get this:
Here is jsfiddle.
Documentation for second parameter of rangePoints() is here. Illustration for parameter meaning:
I am attempting to build a very basic bar chart. Three bars. The data is correct. However, the bars do not display, nor are they showing up when I inspect the barchart with dev tools. However, the x-axis and y-axis render correctly, with the correct labels and tick marks.
function buildBarChart(data) {
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.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");
var chart = d3.select(".barchart").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 mapped_values = _.map(data, function(d) {
return d.count;
});
x.domain(d3.keys(data));
y.domain([0, d3.max(mapped_values)]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.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("Count");
_.each(data, function(d) {
console.log(d.name + " ++ " + (height - y(d.count)) + " ++ " + d.count);
});
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name) })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); });
};
The result of the console.log above is:
quotes ++ 90 ++ 16
subscriptions ++ 450 ++ 80
sponsored_licenses ++ 45 ++ 8
which is correct. I have been starting at this for hours and cannot figure out why everything displays (including the line charts on the same page) except for the bars.
Why are my bars not displaying?
The data you assign to a selection has to be an array, not a key:value object. You can use the d3.values(object) utility function to extract just the values as an array.
chart.selectAll(".bar")
.data(d3.values(data))
/* etc. */
However, the key values for the objects are no longer available in that form, and I notice that you're using them for the x-axis. If the keys are the same as the "name" of the data object that shouldn't be a problem. If not, the d3.entries() utility function returns both keys and values, but the values become a nested object, so you'd have to use d.value.name and d.value.count to access the original data.