I'm trying to create a heat map.
The code is on Codepen: https://codepen.io/imestin/pen/qBOpodX?editors=1010
My problem is, that the axes are not showing on the SVG canvas.
Inspecting it with web developer tools, I can see the elements.
These are the lines that I think are important in drawing the axes (for the x-axis):
let minYear = d3.min(data, d => d.year );
let maxYear = d3.max(data, d => d.year );
let xScale = d3.scaleLinear()
.domain(minYear,maxYear)
.range(0,canvasX);
//xAxis - needs xScale
let xAxis = d3.axisBottom(xScale);
//X-axis draw
Canvas.append("g")
.attr("id","x-axis")
.attr("class","tick")
.attr("transform", "translate(" + (edge) + ", " + (canvasY) + ")")
.call(xAxis)
Other elements are drawing correctly, basic SVG drawing is working.
The xScale domain and range need an array of values, for example:
xScale = d3.scaleLinear()
.domain([minYear,maxYear])
.range([0,canvasX]);
And secondly, the axis is positioned off the bottom of canvas, so it needs to adjusted up, for example:
Canvas.append("g")
.attr("id","x-axis")
.attr("class","tick")
.attr("transform", "translate(" + (edge) + ", " + (canvasY - 20) + ")")
.call(xAxis)
Related
I'm trying to create an area chart for statistics on each US state. I have a single number statistic for each state; an element of my data list looks like the following:
{'state':'CA','count':4000}
Currently, my area chart looks like this. The task is mainly complete, but you may notice how the very last category (in this case, UTAH) isn't filled. I'm not quite sure how to get around this. close_up
I am using a scaleBand axis; this felt appropriate. Perhaps it is not the correct approach. Here is the JS behind the chart:
var svg_area = d3.select("#area")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom),
g_area = svg_area.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand().range([0, width]),
y = d3.scaleLinear().range([height, 0]);
var area = d3.area()
.x(function(d) { return x(d.state); })
.y0(height)
.y1(function(d) { return y(d.count); });
d3.csv('data/states.csv', function(data) {
data.forEach(function(d) {
d.count = +d.count;
});
data.sort(function(a, b){
return b.count-a.count;
});
data = data.slice(0,30);
x.domain(data.map(function(d) { return d.state; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
g_area.append('path')
.datum(data)
.attr('fill', solar[1])
.attr("class", "area")
.attr('d', area);
g_area.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g_area.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(0," + 0 + ")")
.call(d3.axisLeft(y));
});
Any suggestions on how I can fix this? Thanks for any feedback!
Contrary to your question's title (now edited), the area chart is not "leaving out the last data point".
What you're seeing is the expected result, since you are using a band scale. Actually, that value just above the horizontal axis (just in the "edge" of the area chart) is Utah value! Try to understanding it with this explanation: Imagine a bar chart with your data. Each bar has, of course, a given width. Now, draw a path going from the top left corner of one bar to the top left corner of the next bar, starting at the first bar and, when reaching the last bar, going down from the top left corner to the axis. That's the area you have right now.
There are two solutions here. The first one is using a point scale instead:
var x = d3.scalePoint().range([0, width])
However, this will trim the "margins" of the area path, before the first state and after the last state (Utah). That means, the area chart will start right over California tick and end right over Utah tick.
If you don't want that there is a second solution, which is hacky, but will keep those "margins": add the bandwidth() to the last state in the area generator:
var area = d3.area()
.x(function(d, i) {
return i === data.length - 1 ?
x(d.state) + x.bandwidth() : x(d.state)
})
It may be worth noting that, using a band scale, your chart is technically incorrect: the values in the area for each state are not over the tick for that state.
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
I have a current zoom function I just learned to use in D3. However when I use it, it only moves my and zooms the axis of the graph not the objects on it.
I'm very knew to D3 and would like some help please.
My source code of the javascript is posted below:
//Setting generic width and height values for our SVG.
var margin = {top: 60, right: 0, bottom: 60, left: 40},
width = 1024 - 70 - margin.left - margin.right,
height = 668 - margin.top - margin.bottom;
//Other variable declarations.
//Creating scales used to scale everything to the size of the SVG.
var xScale = d3.scale.linear()
.domain([0, 1024])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([1, 768])
.range([height, 0]);
//Creates an xAxis variable that can be used in our SVG.
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
//Zoom command ...
var zoom = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([1, 10])
.on("zoom", zoomed);
// The mark '#' indicates an ID. IF '#' isn't included argument expected is a tag such as "svg" or "p" etc..
var SVG = d3.select("#mainSVG")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
//Create background. The mouse must be over an object on the graph for the zoom to work. The rectangle will cover the entire graph.
var rect = SVG.append("rect")
.attr("width", width)
.attr("height", height);
//This selects 4 circles (non-existent, there requires data-binding) and appends them all below enter.
//The amount of numbers in data is the amount of circles to be appended in the enter() section.
var circle = SVG
.selectAll("circle")
.data([40,100,400,1900])
.enter()
.append("circle")
.attr("cx",function(d){return xScale(d)})
.attr("cy",function(d){return xScale(d)})
.attr("r",20);
//This appends a circles to our SVG.
var circle = SVG
.append("circle")
.attr("cx",function(d){ return xScale(d)})
.attr("cy",300)
.attr("r",20);
//Showing the axis that we created earlier in the script for both X and Y.
var xAxisGroup = SVG.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
var yAxisGroup = SVG.append("g")
.attr("class", "y axis")
.call(yAxis);
function zoomed() {
SVG.select(".x.axis").call(xAxis);
SVG.select(".y.axis").call(yAxis);
}
You also need to redraw all the elements with the changed axes on zoom -- D3 won't do this for you automatically:
function zoomed() {
SVG.select(".x.axis").call(xAxis);
SVG.select(".y.axis").call(yAxis);
SVG.selectAll("circle")
.attr("cx",function(d){return xScale(d)})
.attr("cy",function(d){return xScale(d)});
}
I work with D3JS and I want an axis in x with this kind of values : 125, 250, 500, 1000 ... So multiply by 2 my values each time.
So I tried a Quantize Scales like this:
var qScale = d3.scale.quantize(2)
.domain([0, 8000])
.range([0, 500]);
And I create my axis like that :
var xAxis = d3.svg.axis()
.scale(qScale);
But when I'm calling the axis with that code :
svg.append("g")
.attr("transform", "translate(" + padding + "," + (ChartHeight - padding) + ")")
.attr("class", "axis")
.call(xAxis);
I have the following error :
Object doesn't support property or method 'rangeBand' (I develop on Visual Studio 2012)
Any idea ?
EDIT: here the full code code
I am trying to create a d3 chart with logarithmic scale(1,10,100..). I used their functions and call but not much help.
var x = d3.scale.log()
.domain([1,10])
.range([0, width]);
var y = d3.scale.log()
.domain([1,10])
.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 line = d3.svg.line()
.x(function(d) { return x(d[xAxxis]); })
.y(function(d) { return y(d[yAxxis]); });
var svg = d3.select("#"+this.divid).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(d3.extent(totalArray, function(d) { return d[xAxxis]; })).nice();
y.domain(d3.extent(totalArray, function(d) { return d[yAxxis]; })).nice();
Is there any way i can create the axis with 1e+0,10e+0,100e+0 rather than 1e+0,2e+0,3e+0
You can control which labels the axis renders using either the tick() or tickValues() method, depending on how you want to go about it. See the d3's API documentation for more details. Also, note that a log scale's tick() method takes no arguments, so you'll likely have to use tickValues() though the other method might come in handy for formatting.