I have two axes in my graph right now, that are stuck at the very left and bottom of the graph. I want to make the axes line up with the (0,0) coordinate, or in other words I want the axes to be at x=0 and y=0
Here's my axes code:
//setup x
var xAxis = d3.svg.axis()
.scale(xRange)
.tickSize(5)
.tickSubdivide(true),
//setup y
yAxis = d3.svg.axis()
.scale(yRange)
.tickSize(5)
.orient("left")
.tickSubdivide(true);
I was thinking that the way to do it might just be to make a smaller svg underneath the one that I have, that starts at zero, and put the axes there, and remove them from the one I have right now.
Here's the full code: http://jsfiddle.net/v92q26L8/
The key part of your code is the bit where you attach the axes
vis.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
.call(xAxis);
vis.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
.call(yAxis);
At the moment you are positioning the axes bottom and left using transforms on the groups (svg:g nodes) which contain them.
To reposition the axes you simply need to adjust these transforms using your defined scales.
For your x axis
.attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
becomes
.attr("transform", "translate(0," + yRange(0) + ")")
for your y axis
.attr("transform", "translate(" + (MARGINS.left) + ",0)")
becomes
.attr("transform", "translate(" + xRange(0) + ",0)")
Additionally, it may be sensible to change the names of your scales. The term 'range' has a very particular meaning in D3, I'd suggest xScale and yScale.
JS Fiddle
Related
In this axis label example, which uses D3 v4, it adds the x axis and the text label as separate nodes under svg.
// Add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label for the x axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Date");
When I chain the code above (hence moving the text element under the x axis group):
// Add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.style("text-anchor", "middle")
.text("Date");
Then my axis title is not visible any more (see screenshot below). I can still find my text element in DOM, under the x axis group, but it's not there in the rendered HTML.
I want to know:
Is it by design that D3 wants me to add axis and its label separately (i.e., not chaining)?
Why is my text element not visible after I move it under the x axis group?
D3 axis label has to be added separately?
No, it doesn't. You can chain, that's not the problem. The problem here is the fill of the text element.
As you can see in the screenshot you linked, the container group has "none" as fill. Since the text inherits the parent's attributes/styles, you'll have to change its fill from "none" to any color you want:
var svg = d3.select("svg");
var xScale = d3.scaleLinear()
.domain([1, 10])
.range([10,390]);
var xAxis = d3.axisBottom(xScale);
var gX = svg.append("g")
.attr('class', 'axis')
.attr("transform","translate(0,40)")
.call(xAxis)
.append("text")
.attr("fill", "black")//set the fill here
.attr("transform","translate(120, 40)")
.text("Hello World!!!");
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="400" height="80"></svg>
PS: this problem wouldn't happen in D3 v3.x.
I am using D3 v4. I have a bar graph created with an x-axis using scaleBand(). Now, I have created a y-axis but my issue is that no matter how I position it, it is cutting into the actual bars of the graph.
At the top of my JS file, I have:
var width = 350;
var height = 300;
Then, the part where I actually create the Y-axis:
var y = d3.scaleLinear()
.domain([0, 300000])
.rangeRound([height, 0]);
var yAxis = d3.axisRight(y);
yAxis.ticks(6);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (width - dist_from_right) + ", 0)")
.call(yAxis);
As you can see from the picture, the axis stretches the entirety of the height of the SVG, from bottom to top meaning that half of the 0 gets cut off and half off the 300,000 gets cut off.
First question: how do I "squish" (or scale) the y-axis so that it displays within the confines of the SVG?
Next, I want to translate the y-axis so that it is not cutting into my red bar. If I try to use the transform attribute, I can push the axis far to the right of the SVG borders but that means the numbers are off the SVG boundary. I've also tried to increase the width variable but that does nothing because it just stretches out the x-axis proportionally.
Second question: how do I move the y-axis so that it is not cutting into the x-axis and red bar and also remains visible in the SVG window?
Thanks!
In D3, axes are positioned according to the range of corresponding scales. So, you need a "padding" for the ranges. Right now, as your range goes from 0 to height (or vice versa, it doesn't matter), the axis starts at the very beginning of the SVG and ends at its very end.
I see you have a dist_from_right, but I don't know what are you doing with it in your x scale. So, for now, let's suppose you don't have any padding.
First, let's set the paddings:
var paddingLeft = 10, paddingRight = 10, paddingTop = 10, paddingBottom = 10;
Here, 10 is just a given number, change it accordingly.
After that, set the ranges using the paddings:
var y = d3.scaleLinear()
.domain([0, 300000])
.rangeRound([height - paddingBottom, paddingTop]);
The same for your x scale:
var x = d3.scaleLinear()
.domain([0, someValue])
.rangeRound([paddingLeft, width - paddingRight]);
Then you define the axis:
var xAxis = d3.axisBottom(x);//the same for the y axis
Having the ranges with the paddings, call the axes setting that paddings:
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (width - paddingRight) + ", 0)")
.call(yAxis);
And the same for the x axis:
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - paddingBottom) + ")")
.call(xAxis);
Here is a working example. I made the SVG light gray and the plotting area white, so you can see the paddings.:
var paddingLeft = 20, paddingRight = 40, paddingTop = 10, paddingBottom = 40;
var width = 300, height = 300;
var chart = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
chart.append("rect")
.attr("x", paddingLeft)
.attr("y", paddingTop)
.attr("width", width - paddingLeft - paddingRight)
.attr("height", height - paddingTop - paddingBottom)
.attr("fill", "white");
var y = d3.scaleLinear()
.domain([0, 100])
.rangeRound([height - paddingBottom, paddingTop]);
var x = d3.scaleLinear()
.domain([0, 100])
.rangeRound([paddingLeft, width - paddingRight]);
var yAxis = d3.axisRight(y);
var xAxis = d3.axisBottom(x);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + (width - paddingRight) + ", 0)")
.call(yAxis);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - paddingBottom) + ")")
.call(xAxis);
svg {
background-color: lightgray;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
I have a bar chart with the X axis, which have ticks that include the month and year. The month and year are in the single row now.
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(RU.timeFormat("%b %Y"));
I need to leave the month in the first line and move the year in the second one.
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
Here is my jsfiddle: https://jsfiddle.net/anton9ov/uaygrmLo/
There is this nice "wrap text function", written by Bostock: https://bl.ocks.org/mbostock/7555321
Using this function, I just modified your axis:
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, 40);
Check the fiddle: https://jsfiddle.net/gerardofurtado/aLc8t6yq/
PS: I used 40 here as a magic number, change it according to your needs.
I'm trying to have the tick marks show up on both sides of the y axis. As the code is shown below, I'm able to extend the tick marks based on length -width which draws the tick mark from a starting point from left to right.
Is it possible to move the starting point of the tick mark further to the left?
My intent is aesthetics, but to have the tick values be directly over a length of the tick marks.
I have a grid set up, made up of container-length tick marks:
// Axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width)
.tickFormat(d3.format("s"));
Based on these scales:
// Scale
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
My axis are appended to the svg like this:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.call(adjustXLabels);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.call(adjustYLabels);
I'm a little confused about what you're after. Do you want the tick labels to overlap with the ticks themselves? If so you can select the text after the axis is drawn and then translate the ticks.
See the fiddle here: https://jsfiddle.net/6q3rpw6j/
The key bit is:
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll(".tick text")
.attr("transform", "translate(15,0)");
EDIT
To move the ticks themselves:
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll(".tick line")
.attr("transform", "translate(15,0)");
And to remove the top and bottom end ticks, make sure you set the .outerTickSize(0)
I am trying to develop a scatterplot using d3 but the domain for y-axis is confusing me. y-axis are gonna display patient names and x-axis display their appointment dates. x-axis are working fine, but y-axis are displaying only two patient names.
function graph() {
var num_patient = Object.keys(patientList).length;
var patient_names = Object.keys(patientList);
console.log(patient_names);
var x = d3.time.scale().range([0, width]);
var y = d3.scale.ordinal().range([height, 0]);
x.domain(d3.extent(data, function(d) {return parseDate(d.dates); }));
//y.domain(patient_names.map(function(d) { return d.name;}));
y.domain(patient_names);
console.log(y.domain());
var xAxis = d3.svg.axis()
.scale(x)
.ticks(d3.time.year, 1)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("#punchcard")
.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.selectAll("dot")
.data(data)
.enter()
.append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.value); });
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
}
console.log(patient_names) display the names correctly:
`["Andrew","Fred","Steve","John"]`
console.log(y.domain()) displays an extra undefined object:
["Andrew", "Fred","Steve" , "John", undefined]
But the y-axis only display Andrew at 0 and Fred at height h. How can I get to display all four names? I cannot hard code them as they are user input values. BTW: I am a beginner with d3 and js.
Thanks in advance!
With ordinal scales, you need to define the range points for the inputs explicitly (see the documentation). That is, you need to tell the scale explicitly which input value to map to which output. For example:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.range([height, height * 2/3, height * 1/3, 0]);
You probably want to use the .rangePoints() method instead, which allows you to specify an interval that D3 automatically divides based on the number of values in the domain:
var y = d3.scale.ordinal()
.domain(["Andrew","Fred","Steve","John"])
.rangePoints([height, 0]);
Note that for .rangePoints() to work properly, you need to set the domain before the output range.