reversed Y-axis D3 - javascript

I created a bar plot and my Y axis is reversed somehow (also seems like the scale is not right). I really couldn't figure it out by myself. Here is the link for Jsfiddle.
This is the code I am working on, in case you want to try it elsewhere.
var w = 700;
var h = 400;
var margin = 40;
var dataset = [
{key:1,value:4000},
{key:2,value:3500},
{key:3,value:4400},
{key:4,value:3250},
{key:5,value:4785},
{key:6,value:3600},
{key:7,value:3200}
];
var key = function(d) {
return d.key;
};
var value = function(d) {
return d.value;
};
console.log(dataset);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length+1))
.rangeRoundBands([40, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, 5000])
.range([0, h-40]);
var x_axis = d3.svg.axis().scale(xScale);
var y_axis = d3.svg.axis().scale(yScale).orient("left");
d3.select("svg")
.append("g")
.attr("class","x axis")
.attr("transform","translate(0,"+(h-margin)+")")
.call(x_axis);
d3.select("svg")
.append("g")
.attr("class","y axis")
.attr("transform","translate("+margin+",0)")
.call(y_axis);
//Create bars
svg.selectAll("rect")
.data(dataset, key)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d.value);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d.value)-margin;
})
.attr("fill", function(d) {
return "rgb(96, 0, " + (d.value * 10) + ")";
})
//Tooltip
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x")) + xScale.rangeBand() / 2;
var yPosition = parseFloat(d3.select(this).attr("y")) + 14;
//Update Tooltip Position & value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#value")
.text(d.value);
d3.select("#tooltip").classed("hidden", false)
})
.on("mouseout", function() {
//Remove the tooltip
d3.select("#tooltip").classed("hidden", true);
}) ;
//Create labels
svg.selectAll("text")
.data(dataset, key)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d.value) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");

Maybe this will help.
http://jsfiddle.net/S575k/4/
A few notes:
I reversed your y domain range from .range([0, h-margin]); to .range([h-margin, 0]); This will fix the issue of the y-axis marks going in the wrong direction because the browser consider's the origin (0,0) point to the the upper-left corner, and not the bottom-left corner like in math.
Because of this reversal I had to tweak your .attr('height') and .attr('y').
A nice way to find the height of bar in a bar chart is to realize that the yscale(0) will give you the pixel-position of the bottom of the barchart. You can then do yscale(value) - yscale(0) to get the pixel-height of your bars.

Related

D3.js treemap cells overlap with each other

When I draw my treemap in the local server, the treemap cells overlap with each other. Ive tried using other tiling algorithms but that did not work. I have also tried messing round with my linear scales but I can seem to get the correct scaling. Would this be a scaling problem or something else. I have also messed with the transform attributes but that just made it even worse. The current one I have is the best one that I find works.
createTreemap() {
//***VARS */
var margin = { left: 100, right: 10, top: 10, bottom:100}
var svg = d3.select("svg")
//.attr("transform", function(d) { return "translate(" + margin.left + "," + margin.top+ ")"; });
var width = +svg.attr("width") - margin.left - margin.right //width = 960
var height = +svg.attr("height") - margin.top - margin.bottom //height = 570
// var width = +svg.attr("width")
// var height = +svg.attr("height")
//linear scales
var y = d3.scaleLinear()
.domain([0,height])
.range([0,height/2])
var x = d3.scaleLinear()
.domain([0,width])
.range([0,width/2])
var scale = d3.scaleLinear()
.domain([0,width])
.range([0,width/2])
//creating a treemap variable
var treemapLayout = d3.treemap()
.tile(d3.treemapBinary) //type of squares
.size([width/2,height/2]) //size
.round(true) //if number are decimal, round to int. when true
.paddingInner(1) //padding between rectangles (1px)
//****loading data into function****
d3.json("../static/warehouses.json").then(function(data){
var root = d3.hierarchy(data, d => d.warehouses)
.sum(function(d){return d.itemCount}) //formating data to a more complex hierarchy form
treemapLayout(root)//passing data stuct to treemap variable
console.log(treemapLayout(root))//logging to console
//setting canvas sizes
var cells = svg.selectAll("g")
.data(root.leaves())
.enter()
.append("g")
//.attr("transform", function(d) { return "translate(" + scale(d.x0) + "," + scale(d.y0)+ ")"; });
cells.append("rect")
.attr("x", function(d) { return d.x0 })
.attr("y", function(d) { return d.y0 })
.attr("width", function(d) { return d.x1 })
.attr("height", function(d) { return d.y1 })
.attr("fill", "#ccc")
cells.append("text")
.attr("x", function(d) { return d.x0 + 5 })
.attr("y", function(d) { return d.y0 + 15 })
.style("font", "15px monospace")
.text(function(d){ return d.data.name})
.attr("fill", "black")
cells.append("text")
.attr("x", function(d) { return d.x0 +5 })
.attr("y", function(d) { return d.y0 +27 })
.style("font", "10px monospace")
.text(function(d) { return "Item count: " + d.data.itemCount })
.attr("fill", "black")
})
},
To be specific the warehouse "PROVEEDOR" is overlaying or laid over the warehouse "Wec"
image of treemap what would be the cause of this? Because I thought d3 automatically figures out where each x0, y0, x1, y1 should go so they dont overlap?
Thank you for any help
Just learning d3 but ran into the same problem that brought me to this post. I solved it by debugging this code:
cells.append("rect")
.attr("x", function(d) { return d.x0 })
.attr("y", function(d) { return d.y0 })
.attr("width", function(d) { return d.x1 })
.attr("height", function(d) { return d.y1 })
.attr("fill", "#ccc")
with this code...
cell.append("rect")
.attr("width", d=> d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0)

d3 axes not appearing

I'm having a problem getting the axes to actually show on my bar graph, so far without any luck as I just can't seem to wrap my head around what's wrong. Is it something to do with the scaling?
Is the axis rendering but being cut out of the svg by the bars?
<script type="text/javascript">
//Width and height
var w = 850;
var h = 650;
var barpadding = 20;
var dataset = [40, 99];
//Create SVG element
var svg = d3.select(".wrapper")
.append("svg")
.attr("width", w)
.attr("height", h);
//Create scale functions
var x = d3.scaleBand()
.range([0, w])
.padding(0.1);
var y = d3.scaleLinear()
.range([h, 0]);
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 * 4);
})
.attr("width", w / dataset.length - barpadding)
.attr("height", function (d) {
return d * 4;
})
.attr("fill", "dodgerblue");
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function (d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function (d, i) {
return i * (w / dataset.length) + (w / dataset.length - barpadding) / 2;
})
.attr("y", function (d) {
return h - (d * 4) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "18px")
.attr("fill", "black");
d3.select(".axis")
.call(d3.axisBottom(x));
</script>
For showing the axis, you'll have to append a <g> element first. After that, since the axes are always generated at the origin (0,0), you'll have to translate it down and, only then, calling the axis:
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + someValue + ")")
.call(d3.axisBottom(x));
I normally provide a working demo, but this time I'll skip it, because your code has some problems:
It lacks the margins for the axis
It lacks the domains of the scales
It position the bars using magic numbers, not the scales
But, if you want to see your code with the axis 20px above the bottom, this is how it looks like: https://jsfiddle.net/qcnako3g/

how to left align ticks in d3 bar chart

i created a stacked bar graph.
on the y axis side i have ticks with varying lengths.
what i am trying to accomplish is to align the text in the tick to the left.
this is my example:http://jsfiddle.net/2khbceut/2/
html
<title>Diverging Stacked Bar Chart with D3.js</title>
<body>
<div id="figure" align="center" style="margin-bottom: 50px;"></div>
</body>
javascript
$(document).ready(getTopolegy());
function getTopolegy(){
var data = null;
var links = parseTopology(data);
createChart(links);
}
function parseTopology(data){
var links=[{1:5,2:5,3:10,N:20,link_name: "Link CHGIL21CRS-SFXCA21CRS"},
{1:5,2:5,3:10,N:20,link_name: "Link NYCNY21CRS-NYCNY22CRS"}];
return links;
}
function jsonNameToId(name){
switch (allocated_priority) {
case "allocated_priority":
return 1;
case "allocated_default":
return 2;
case "spare_capacity":
return 3;
case "total":
return "N";
default:
return 999;
}
}
function createChart(data){
var margin = {top: 50, right: 20, bottom: 10, left: 210},
width = 1000 - margin.left - margin.right,
height = 100 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .3);
var x = d3.scale.linear()
.rangeRound([0, width]);
var color = d3.scale.ordinal()
.range(["#cccccc", "#92c6db", "#086fad"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("top");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
var svg = d3.select("#figure").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("id", "d3-plot")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(["Allocated Priority %", "Allocated Default %", "Spare Capacity %"]);
// d3.csv("js/raw_data.csv", function(error, data) {
data.forEach(function(d) {
d["Allocated Priority %"] = +d[1]*100/d.N;
d["Allocated Default %"] = +d[2]*100/d.N;
d["Spare Capacity %"] = +d[3]*100/d.N;
var x0 = 0;
var idx = 0;
d.boxes = color.domain().map(function(name) { return {name: name, x0: x0, x1: x0 += +d[name], N: +d.N, n: +d[idx += 1]}; });
});
var min_val = d3.min(data, function(d) {
return d.boxes["0"].x0;
});
var max_val = d3.max(data, function(d) {
return d.boxes["2"].x1;
});
x.domain([min_val, max_val]).nice();
y.domain(data.map(function(d) { return d.link_name; }));
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
var vakken = svg.selectAll(".Link")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(0," + y(d.link_name) + ")"; });
var bars = vakken.selectAll("rect")
.data(function(d) { return d.boxes; })
.enter().append("g").attr("class", "subbar");
bars.append("rect")
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.x0); })
.attr("width", function(d) { return x(d.x1) - x(d.x0); })
.style("fill", function(d) { return color(d.name); });
bars.append("text")
.attr("x", function(d) { return x(d.x0); })
.attr("y", y.rangeBand()/2)
.attr("dy", "0.5em")
.attr("dx", "0.5em")
.style("font" ,"10px sans-serif")
.style("text-anchor", "begin")
.text(function(d) { return d.n !== 0 && (d.x1-d.x0)>3 ? d.n : "" });
vakken.insert("rect",":first-child")
.attr("height", y.rangeBand())
.attr("x", "1")
.attr("width", width)
.attr("fill-opacity", "0.5")
.style("fill", "#F5F5F5")
.attr("class", function(d,index) { return index%2==0 ? "even" : "uneven"; });
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x(0))
.attr("x2", x(0))
.attr("y2", height);
var startp = svg.append("g").attr("class", "legendbox").attr("id", "mylegendbox");
// this is not nice, we should calculate the bounding box and use that
var legend_tabs = [0, 150, 300];
var legend = startp.selectAll(".legend")
.data(color.domain().slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(" + legend_tabs[i] + ",-45)"; });
legend.append("rect")
.attr("x", 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 22)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "begin")
.style("font" ,"10px sans-serif")
.text(function(d) { return d; });
d3.selectAll(".axis path")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
d3.selectAll(".axis line")
.style("fill", "none")
.style("stroke", "#000")
.style("shape-rendering", "crispEdges")
var movesize = width/2 - startp.node().getBBox().width/2;
d3.selectAll(".legendbox").attr("transform", "translate(" + movesize + ",0)");
// });
}
as can be seen the current positioning of the tick text is to the right.
i tried the following idea:
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("text-anchor", "start");
but it did not position the ticks in the desired alignment.
any ideas?
You can make the Y axis right-oriented, which will have the effect of positioning all the labels to the right of the axis, left-aligning them:
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")// <- 1st step
At that point the labels would disappear, because they'll get covered up by the bars of the graph.
But then you can shift all those left-aligned labels some constant distance in the negative X direction, such that they're back on the left side of the Y axis, but still left-aligned the way you wanted. tickPadding() is a way to shift them:
var yAxis = d3.svg.axis()
.scale(y)
.orient("right")
.tickPadding(-180)
Here's your example, modified with the above: http://jsfiddle.net/2khbceut/3/
Maybe hardcoding the -180 is ok for you. If you need that amount to be dynamic, you can compute it using getBBox() on each text element of the axis and taking the maximum width to be the negative offset.
You can set the text-anchor to "start" and adjust the x position with translate, I added the code below in the chart model "boxPlotChart.js"
g.select('.nv-y.nv-axis').selectAll('.tick').selectAll('text')
.style('text-anchor','start')
.attr('transform', function(d,i,j) { return 'translate(-14,0)' });
g.select('.nv-y.nv-axis').selectAll('.nv-axisMaxMin').selectAll('text')
.style('text-anchor','start')
.attr('transform', function(d,i,j) { return 'translate(-16,0)' });

Trying to get values from csv into Two-sided horizontal bar-chart

I'm new to d3.js and am trying to create my own two-sided bar-chart. The chart it is based on is http://jasonneylon.wordpress.com/2013/09/05/two-sided-horizontal-barchart-using-d3-js/
I keep coming up with a blank screen whenever I run it, and am unsure as to what I am doing wrong.
var names = [];
var total = [];
var otherValue = [];
d3.text("results.csv",function(data){
var data = d3.csv.parseRows(data);
for( var a = 1;a <data.length;a++){
var names.push(data[a][0]);
var total.push(parseFloat(data[a][1]));
var otherValue.push(parseFloat(data[a][2]));
}
var labelArea = 160;
var chart,
width = 400,
bar_height = 20,
height = bar_height * (data.length);
var rightOffset = width + labelArea;
var chart = d3.select("body")
.append('svg')
.attr('class', 'chart')
.attr('width', labelArea + width + width)
.attr('height', height);
var xFrom = d3.scale.linear()
.domain([0, d3.max(otherValue)])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(names)
.rangeBands([10, height]);
var yPosByIndex = function(d, index){ return y(index); }
chart.selectAll("rect.left")
.data(otherValue)
.enter().append("rect")
.attr("x", function(pos) { return width - xFrom(pos); })
.attr("y", yPosByIndex)
.attr("class", "left")
.attr("width", xFrom)
.attr("height", y.rangeBand());
chart.selectAll("text.leftscore")
.data(otherValue)
.enter().append("text")
.attr("x", function(d) { return width - xFrom(d); })
.attr("y", function(d, z){ return y(z) + y.rangeBand()/2; } )
.attr("dx", "20")
.attr("dy", ".36em")
.attr("text-anchor", "end")
.attr('class', 'leftscore')
.text(String);
chart.selectAll("text.name")
.data(names)
.enter().append("text")
.attr("x", (labelArea / 2) + width)
.attr("y", function(d){ return y(d) + y.rangeBand()/2; } )
.attr("dy", ".20em")
.attr("text-anchor", "middle")
.attr('class', 'name')
.text(String);
var xTo = d3.scale.linear()
.domain([0, d3.max(total)])
.range([0, width]);
chart.selectAll("rect.right")
.data(total)
.enter().append("rect")
.attr("x", rightOffset)
.attr("y", yPosByIndex)
.attr("class", "right")
.attr("width", xTo)
.attr("height", y.rangeBand());
});
chart.selectAll("text.score")
.data(total)
.enter().append("text")
.attr("x", function(d) { return xTo(d) + rightOffset; })
.attr("y", function(d,z){ return y(z) + y.rangeBand()/2; } )
.attr("dx", -5)
.attr("dy", ".36em")
.attr("text-anchor", "end")
.attr('class', 'score')
.text(String);
});
</script>
Using the 3 arrays was a different idea I tried after not managing to figure it out from anything i had found so far.
The csv file looks like this:
ABC,140,35
DEF,164,45
GHI,89,15
JKL,56,20
MNO,20,31
Thanks for any help

X & Y Co-ordinates of selective bars in a stack graph

I have a stacked bar chart. And I'd like a draw a line like grouping few bars of a stacked bar chart.
Something like this.
So to do this I need to find the y coordinate of the 2nd and the last bar.
Some one please help in drawing those line using d3.
JS:
var width = 750,
height = 500;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#D70B16", "#154CEF", "#1A8A55"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "Value: " + (d.y1 - d.y0) + "";
})
var svgContainer = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 30 + "," + 30 + ")");
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "circle"; }));
data.forEach(function(d) {
var y0 = 0;
d.hours = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d3.select('body').append('pre')
.text(JSON.stringify(d.hours, null, ' '));
d.total = d.hours[d.hours.length - 1].y1;
});
x.domain(data.map(function(d) {return d.circle;}));
y.domain([0, d3.max(data, function(d) {return d.total;})])
svgContainer.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svgContainer.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("Login Hours");
var circle = svgContainer.selectAll(".circle")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.circle) + ",0)"; });
circle.selectAll("rect")
.data(function(d) { return d.hours; })
.enter()
.append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
.style("fill", function(d) { return color(d.name); });
circle.selectAll("text")
.data(function(d) { return d.hours; })
.enter()
.append("text")
.attr("x", 75)
.attr("y", function(d, i) { return y(d.y1) ; })
.style("text-anchor", "middle")
.text("test")
})
How you determine those values (and the bars) depends on how you can identify them. In this particular case, you don't actually need to get the rect items because the underlying data gives you access to everything you need.
To, for example, get the y coordinate of the second bar in the first column, you can use the following code:
var yCoord = y(data[0].hours[1].y1);

Categories

Resources