d3 graph with limited zoom - javascript

I have implemented a d3 line graph which reads data from a CSV file, and then plots multiple lines which react to mouseover events. It works fine with pan and zoom using the following code (sorry that it is so long and slightly untidy but I felt it best to display the full code):
d3.csv("ijisb.csv", function(error, data) {
var margin = {top: 50, right: 120, bottom: 50, left: 70},
width = 1200 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
var parseDate = d3.time.format("%m%d").parse;
var color = d3.scale.category20();
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var sources = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, temperature: +d[name]};
})
};
});
var x = d3.time.scale()
.range([0, width])
.domain([
d3.min(data, function(d) { return d.date; }),
d3.max(data, function(d) { return d.date; })
]);
var y = d3.scale.linear()
.range([height, 0])
.domain([
0,
d3.max(sources, function(c) { return d3.max(c.values, function(v) { return v.temperature; }); })
]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(12)
.tickFormat(d3.time.format("%b %d"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var line = d3.svg.line()
.defined(function(d) { return d.temperature >= 0; })
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
var zoom = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([1, 8])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", "0 0 1200 800")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var rect = svg.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("class", "plot");
var make_x_axis = function () {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(12)
.tickFormat(d3.time.format("%b %d"));
};
var make_y_axis = function () {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
};
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("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.append("g")
.attr("class", "y grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
var source = svg.selectAll(".source")
.data(sources)
.enter().append("g")
.attr("class", "source");
var clip = svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.append("text");
source.append("path")
.data(sources)
.attr("class", "line")
.attr("clip-path", "url(#clip)")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) {return color(d.name);})
.style("opacity", 0.8)
.on("mouseover", function(d){
d3.select(this)
.style("stroke",function(d) {return color(d.name);})
.style("opacity", 1.0)
.style("stroke-width", 2.5);
this.parentNode.parentNode.appendChild(this.parentNode);
d3.select('#text-' + d.name)
.style("fill",function(d) {return color(d.name);})
.style("font-weight", 700);
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(250)
.style("stroke", function(d) {return color(d.name);})
.style("stroke-width", 1.5)
.style("opacity", 0.8);
d3.select('#text-' + d.name)
.transition()
.duration(250)
.style("fill", function(d) {return color(d.name);})
.style("font-weight", 400);
})
.attr("id", function(d, i) { return "path-" + d.name; });
source.append("text")
.datum(function(d) { return {name: d.name}; })
.attr("x", function(d, i) { return width+10; })
.attr("y", function(d, i) { return (i*(height/16)); })
.style("fill", function(d) {return color(d.name);})
.on("mouseover", function(d){
d3.select('#path-' + d.name)
.style("stroke",function(d) {return color(d.name);})
.style("opacity", 1.0)
.style("stroke-width", 2.5);
this.parentNode.parentNode.appendChild(this.parentNode);
d3.select(this)
.style("fill",function(d) {return color(d.name);})
.style("font-weight", 700);
})
.on("mouseout", function(d) {
d3.select('#path-' + d.name)
.transition()
.duration(250)
.style("stroke", function(d) {return color(d.name);})
.style("stroke-width", 1.5)
.style("opacity", 0.8);
d3.select(this)
.transition()
.duration(250)
.style("fill", function(d) {return color(d.name);})
.style("font-weight", 400);
})
.text(function(d) { return d.name; })
.attr("font-family","sans-serif")
.attr("font-size","14px")
.attr("id", function(d, i) { return "text-" + d.name; }
);
var minT = new Date('01/01/1900'), maxT = new Date('01/01/2002'), w = $(window).width();
function zoomed() {
d3.event.translate;
d3.event.scale;
svg.select(".x.axis")
.call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
source.select(".line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) {return color(d.name);});
}
});
The problem I have is that I want to limit the panning and zooming so that it is not possible for the graph to be zoomed or panned out offscreen. I can do this by setting the scaleExtent on the zoom (which is implemented in the above example) and changing the zoomed function to:
function zoomed() {
var t = zoom.translate(),
tx = t[0];
ty = t[1];
tx = Math.min(tx, 0);
zoom.translate([tx, ty]);
d3.event.translate;
d3.event.scale;
svg.select(".x.axis")
.call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".x.grid")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat(""));
svg.select(".y.grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat(""));
source.select(".line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) {return color(d.name);});
}
This limits the x-axis minimum to zero. However hard I try however, I cannot limit the maximum value of the x-axis, which means the graph can still pan too far to the right.
Any help? I hope this makes sense!
Nick

Thanks for the help, I did it by the following code in the end:
var t = zoom.translate(),
s = zoom.scale();
tx = Math.min(0, Math.max(width * (1 - s), t[0]));
ty = Math.min(0, Math.max(height * (1 - s), t[1]));
zoom.translate([tx, ty]);
The difficulty was in bounding the graph at various zoom levels, which this now solves. The d3.event.translate and d3.event.scale statements were also unnecessary, and should have been removed.

You can simply check the value manually and reset it if it is too high:
if(tx > threshold) { tx = threshold; }
Also, the statements
d3.event.translate;
d3.event.scale;
in your code have no effect.

Related

Cannot properly draw a scatterplot over a grouped bar chart in D3

I have a set of nested json data:
var data = [{"time":"2016-03-01","values":[{"specimen_count":1,"trap":"S0024", "species":1},{"specimen_count":2,"trap":"S0025", "species":2},{"specimen_count":2,"trap":"S0026", "species":2}]},{"time":"2016-03-15","values":[{"specimen_count":6,"trap":"S0024", "species":6},{"specimen_count":5,"trap":"S0025", "species":4},{"specimen_count":7,"trap":"S0026", "species":6}]}];
And I want to draw a set of grouped bar charts each group representing a time interval and each group with 3 bars, each representing a trap, and the height of the bar is the specimen_count field.
Now I want to add a scatterplot, one dot for each bar and the height of the dot is the species field, using the same scales. But I am having trouble successfully placing the dots on top the the grouped bar chart. I did manage to add a line with the species data, but I can't add the dots using the same logic.
Here is my code:
var margin = {top: 100, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020","#f4a582","#92c5de"]);
var svg = d3.select('#chart').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 categoriesNames = data.map(function(d) { return d.time; }); // the 5 time periods
var trapNames = data[0].values.map(function(d) { return d.trap; }); // the name of the traps
console.log(trapNames);
x0.domain(categoriesNames);
x1.domain(trapNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(category) { return d3.max(category.values, function(d) { return d.specimen_count; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "slice")
.attr("transform",function(d) { return "translate(" + x0(d.time) + ",0)"; });
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.trap); })
.style("fill", function(d) { return color(d.trap) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.trap)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.trap));
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.specimen_count); })
.attr("height", function(d) { return height - y(d.specimen_count); });
var valueline = d3.svg.line()
.x(function (d) { return x1(d.trap) + x1.rangeBand()/2; })
.y(function (d) { return y(d.species); });
slice.enter()
.append('path')
.attr('class','line')
.style('stroke', "#0571b0")
.style('stroke-width', "3px")
.attr('fill', 'none')
.attr('d', function(d) { return valueline(d.values); });
slice.selectAll('.dot').data(data,function(d){return d.time;})
.enter()
.append("circle")
.attr("class", "dot")
.attr("r",5)
.attr("cx", function(d){
return x1(d.trap) + x1.rangeBand()/2;
})
.attr("cy",function(d){
return y(d.species);
})
.attr("fill","#0571b0");
There error I'm getting from the circle-related code is: d3.min.js:1 Error: attribute cx: Expected length, "NaN".
I think the nested data and the ordinal scale for the bar chart is throwing me off a bit, so it could be that I am not understanding fulling data access in these cases.
Also here is the screenshot of the current graph
If you need the dots on every bar chart, then the data() callback must return a list of bars not a single item. Did you try replacing it with:
slice.selectAll('.dot')
.data(function(d) {
return d.values;
})
.enter()
.append("circle") //... and so on
Doing this will use the existing data object (with 5 bar groups), but render a dot for each bar.
Here it is running:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var data = [{
"time": "2016-03-01",
"values": [{
"specimen_count": 1,
"trap": "S0024",
"species": 1
}, {
"specimen_count": 2,
"trap": "S0025",
"species": 2
}, {
"specimen_count": 2,
"trap": "S0026",
"species": 2
}]
}, {
"time": "2016-03-15",
"values": [{
"specimen_count": 6,
"trap": "S0024",
"species": 6
}, {
"specimen_count": 5,
"trap": "S0025",
"species": 4
}, {
"specimen_count": 7,
"trap": "S0026",
"species": 6
}]
}];
var margin = {
top: 100,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020", "#f4a582", "#92c5de"]);
var svg = d3.select('#chart').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 categoriesNames = data.map(function(d) {
return d.time;
}); // the 5 time periods
var trapNames = data[0].values.map(function(d) {
return d.trap;
}); // the name of the traps
console.log(trapNames);
x0.domain(categoriesNames);
x1.domain(trapNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(category) {
return d3.max(category.values, function(d) {
return d.specimen_count;
});
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.style('opacity', '0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight', 'bold')
.text("Value");
svg.select('.y').transition().duration(500).delay(1300).style('opacity', '1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "slice")
.attr("transform", function(d) {
return "translate(" + x0(d.time) + ",0)";
});
slice.selectAll("rect")
.data(function(d) {
return d.values;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.trap);
})
.style("fill", function(d) {
return color(d.trap)
})
.attr("y", function(d) {
return y(0);
})
.attr("height", function(d) {
return height - y(0);
})
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.trap)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.trap));
});
slice.selectAll("rect")
.transition()
.delay(function(d) {
return Math.random() * 1000;
})
.duration(1000)
.attr("y", function(d) {
return y(d.specimen_count);
})
.attr("height", function(d) {
return height - y(d.specimen_count);
});
var valueline = d3.svg.line()
.x(function(d) {
return x1(d.trap) + x1.rangeBand() / 2;
})
.y(function(d) {
return y(d.species);
});
slice
.append('path')
.attr('class', 'line')
.style('stroke', "#0571b0")
.style('stroke-width', "3px")
.attr('fill', 'none')
.attr('d', function(d) {
return valueline(d.values);
});
slice.selectAll('.dot').data(function(d) {
return d.values;
})
.enter()
.append("circle")
.attr("class", "dot")
.attr("r", 5)
.attr("cx", function(d) {
return x1(d.trap) + x1.rangeBand() / 2;
})
.attr("cy", function(d) {
return y(d.species);
})
.attr("fill", "#0571b0");
</script>
</body>
</html>

Total height of Stacked Bar on tip of every rectangle in stacked Bar Chart using d3

I drawn a stackedbar Chart in which now I trying to place the Total value(I mean the yaxis value) on tip of the every rectangle. I have coded to fetch the details but here the problem is I am getting Every Layer value on tip of every layer but i need to show only the last layer value.
the problem is shown in below fig's.
My code is Shown below
var fData =
[{"orders":"A","Total_Orders":76,"A_Lines":123,"B_Lines":0,"C_Lines":0,"D_Lines":0,"Total_Lines":123,"Total_Units":3267},
{"orders":"B","Total_Orders":68,"A_Lines":0,"B_Lines":107,"C_Lines":0,"D_Lines":0,"Total_Lines":107,"Total_Units":3115},
{"orders":"C","Total_Orders":81,"A_Lines":0,"B_Lines":0,"C_Lines":123,"D_Lines":0,"Total_Lines":123,"Total_Units":3690},
{"orders":"D","Total_Orders":113,"A_Lines":0,"B_Lines":0,"C_Lines":0,"D_Lines":203,"Total_Lines":203,"Total_Units":7863},
{"orders":"AB","Total_Orders":62,"A_Lines":70,"B_Lines":76,"C_Lines":0,"D_Lines":0,"Total_Lines":146,"Total_Units":1739},
{"orders":"AC","Total_Orders":64,"A_Lines":77,"B_Lines":0,"C_Lines":79,"D_Lines":0,"Total_Lines":156,"Total_Units":2027},
{"orders":"AD","Total_Orders":100,"A_Lines":127,"B_Lines":0,"C_Lines":0,"D_Lines":144,"Total_Lines":271,"Total_Units":6467},
{"orders":"BC","Total_Orders":64,"A_Lines":0,"B_Lines":80,"C_Lines":84,"D_Lines":0,"Total_Lines":164,"Total_Units":1845},
{"orders":"BD","Total_Orders":91,"A_Lines":0,"B_Lines":108,"C_Lines":0,"D_Lines":135,"Total_Lines":243,"Total_Units":4061},
{"orders":"CD","Total_Orders":111,"A_Lines":0,"B_Lines":0,"C_Lines":132,"D_Lines":147,"Total_Lines":279,"Total_Units":5011},
{"orders":"ABC","Total_Orders":45,"A_Lines":58,"B_Lines":63,"C_Lines":55,"D_Lines":0,"Total_Lines":176,"Total_Units":1245},
{"orders":"ABD","Total_Orders":69,"A_Lines":105,"B_Lines":87,"C_Lines":0,"D_Lines":116,"Total_Lines":308,"Total_Units":4538},
{"orders":"ACD","Total_Orders":66,"A_Lines":91,"B_Lines":0,"C_Lines":88,"D_Lines":132,"Total_Lines":311,"Total_Units":4446},{
{"orders":"BCD","Total_Orders":68,"A_Lines":0,"B_Lines":84,"C_Lines":95,"D_Lines":111,"Total_Lines":290,"Total_Units":4187},
{"orders":"ABCD","Total_Orders":56,"A_Lines":96,"B_Lines":90,"C_Lines":93,"D_Lines":143,"Total_Lines":422,"Total_Units":6331}]
var headers = ["A_Lines", "B_Lines", "C_Lines", "D_Lines"];
var colors = ["#9999CC", "#F7A35C", "#99CC99", "#CCCC99"];
var colorScale = d3.scale.ordinal()
.domain(headers)
.range(colors);
var layers = d3.layout.stack()(headers.map(function (count) {
return fData.map(function (d, i) {
// alert(d);
return { x: d.ORDER_TYPE, y: +d[count], color: colorScale(count) };
});
}));
//StackedBar Rectangle Max
var yStackMax = d3.max(layers, function (layer) { return d3.max(layer, function (d) { return d.y0 + d.y; }); });
// Set x, y and colors
var xScale = d3.scale.ordinal()
.domain(layers[0].map(function (d) { return d.x; }))
.rangeRoundBands([25, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
// var color = d3.scale.ordinal()
//.domain(headers)
// .range(["#9999CC", "#F7A35C", "#99CC99", "#CCCC99"]);
// Define and draw axes
var xAxis = d3.svg.axis()
.scale(xScale)
.tickSize(1)
.tickPadding(6)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function (d, i) { return colorScale(i); });
var rect = layer.selectAll("rect")
.data(function (d) { return d; })
.enter().append("rect")
.attr("x", function (d) { return xScale(d.x); })
.attr("y", height)
.attr("width", xScale.rangeBand())
.attr("height", 0)
.attr("class", function (d) {
return "rect bordered " + "color-" + d.color.substring(1);
});
layer.selectAll("text.rect")
.data(function (layer) { return layer; })
.enter().append("text")
.attr("text-anchor", "middle")
.attr("x", function (d) { return xScale(d.x) + xScale.rangeBand() / 2; })
.attr("y", function (d) { return y(d.y + d.y0) - 3; })
.text(function (d) { return d.y + d.y0; })
.style("fill", "4682b4");
//********** AXES ************
svg.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", function (d) {
return "rotate(-45)"
});
svg.attr("class", "x axis")
.append("text")
.attr("text-anchor", "end") // this makes it easy to centre the text as the transform is applied to the anchor
.attr("transform", "translate(" + (width / 2) + "," + (height + 60) + ")") // centre below axis
.text("Order Velocity Group");
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(20,0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr({ "x": -75, "y": -70 })
.attr("dy", ".75em")
.style("text-anchor", "end")
.text("No. Of Lines");
//********** LEGEND ************
var legend = svg.selectAll(".legend")
.data(headers.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function (d, i) { return "translate(" + i * (-100) + "," + (height + 50) + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
//.style("fill", color);
.style("fill", function (d, i) { return colors[i]; })
.on("mouseover", function (d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "blue");
})
.on("mouseout", function (d, i) {
svg.selectAll("rect.color-" + colors[i].substring(1)).style("stroke", "white");
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function (d) { return d; });
transitionStacked();
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function (d, i) { return i * 10; })
.attr("y", function (d) { return y(d.y0 + d.y); })
.attr("height", function (d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function (d) { return xScale(d.x); })
.attr("width", xScale.rangeBand());
rect.on('mouseover', tip.show)
.on('mouseout', tip.hide)
};
can anyone help me.
Here is what you need: First, we'll set fData as the data for the texts:
layer.selectAll("text.rect")
.data(fData)
.enter()
.append("text")
But, as this dataset doesn't have the correct yScale value, we'll have to extract it using a filter:
.attr("y", function (d) {
var filtered = fData.filter(function(e){
return e.Total_Units == d.Total_Units
});
return y(filtered[0].Total_Lines) - 3;
})
All together:
layer.selectAll("text.rect")
.data(fData)
.enter().append("text")
.attr("text-anchor", "middle")
.attr("x", function(d) {
return xScale(d.orders) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
var filtered = fData.filter(function(e) {
return e.Total_Units == d.Total_Units
});
return y(filtered[0].Total_Lines) - 3;
})
.text(function(d) {
return d.Total_Units
})
.style("fill", "4682b4");
Here is your fiddle: https://jsfiddle.net/wnwb6meh/

d3 bar chart with custom x axis and bar width

I want to create a bar chart with custom bar width I tried following code but not aware if its the right way to do.
Also I want to update the bar chart with new data how can I do it?
TO update I tried - https://jsfiddle.net/eqr8deef/
var margin = {
top: 25,
right: 40,
bottom: 35,
left: 85
},
w = 500 - margin.left - margin.right,
h = 350 - margin.top - margin.bottom;
var padding = 10;
var colors = {
0: ["Local", "#377EB8"],
1: ["Global", "#4DAF4A"]
};
var dataset = [{
"global": 1468604556084,
"local": 100,
}, {
"local": 11500,
"global": 1313048950629
}, {
"local": 11500,
"global": 1213048950629
}, {
"local": 11500,
"global": 1113048950629
}, {
"local": 11500,
"global": 1123048950629
}, {
"local": 11500,
"global": 1013048950629
}];
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.01);
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d.local;
})])
.range([h, 0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
var commaFormat = d3.format(',');
//SVG element
var svg = d3.select("#searchVolume")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Graph Bars
var sets = svg.selectAll(".set")
.data(dataset)
.enter()
.append("g")
.attr("class", "set")
.attr("transform", function(d, i) {
return "translate(" + xScale(i) + ",0)";
});
sets.append("rect")
.attr("class", "global")
.attr("width", 20)
.attr("y", function(d) {
return yScale(d.local);
})
.attr("height", function(d) {
return h - yScale(d.local);
})
.attr("fill", colors[1][1])
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red");
// yAxis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0 ,0)")
.call(yAxis);
var yTextPadding = 20;
svg.selectAll(".bartext")
.data(dataset)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("x", function(d,i) {
console.log(i, xScale(i))
return xScale(i) + 10;
})
.attr("y", function(d,i) {
return h + 15;
})
.text(function(d){
return new Date(d.global).getFullYear();
});
// xAxis label
http://jsfiddle.net/pq0xrard/
To answer your question step by step -
rangeRoundBands is used to evenly space your bars. But if you want to have custom width then you can not use it like the way you are using it.
to update the data you can simply use enter-update-exit methods as shown below.
var update_sel = svg.selectAll("circle").data(data)
update_sel.attr(/* operate on old elements only */)
update_sel.enter().append("circle").attr(/* operate on new elements
only */)
update_sel.attr(/* operate on old and new elements */)
update_sel.exit().remove() /* complete the enter-update-exit pattern
*/
Here is a complete example - https://jsfiddle.net/seej4dfd/
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")
.ticks(10, "%");
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("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
function draw(data) {
x.domain(data.map(function(d) {
return d.letter;
}));
y.domain([0, d3.max(data, function(d) {
return d.frequency;
})]);
var labels = svg
.selectAll(".topLabel")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "topLabel")
.attr("text-anchor", "middle")
.attr("fill", "black")
labels
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return y(d.frequency);
})
.text(function(d, i) {
return d.letter;
});
var labels = svg
.selectAll(".bartext")
.data(data, function(d) {
return d.letter;
});
labels
.exit()
.remove();
labels
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "middle")
.attr("fill", "black");
labels
.attr("x", function(d, i) {
return x(d.letter) + 7.5;
})
.attr("y", function(d, i) {
return height + 15;
})
.text(function(d, i) {
return d.letter;
});
svg.select(".y.axis").transition().duration(300).call(yAxis)
var bars = svg.selectAll(".bar").data(data, function(d) {
return d.letter;
})
bars.exit()
.transition()
.duration(300)
.remove();
bars.enter().append("rect")
.attr("class", "bar");
bars.transition().duration(300).attr("x", function(d) {
return x(d.letter);
})
.attr("width", 15)
.attr("y", function(d) {
return y(d.frequency);
})
.attr("height", function(d) {
return height - y(d.frequency);
});
}
To change the width, use the xScale.rangeBand() for setting the width of your rect on line 73.
http://jsfiddle.net/073u0ump/3/

D3 Scale domain does not update with selected data. I get negative values

I have a bar chart that updates based on the results selected in a drop-down menu. When I change the selcetion, I get negaitve "y" values. It seems that my domain does not get updated with the new data. When I hard code the domain, my "y" are what I expect them to be. Anyone knows why ? Any other other comments (formatting, etc) welcomed.
var new_data;
//Create SVG margins and patting for the interior
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
//Create Scale
var xScale = d3
.scale
.ordinal()
.rangeRoundBands([margin.left, width], .1);
;
var yScale = d3
.scale
.linear()
.range([height, 0])
;
var xAxis = d3
.svg
.axis()
.scale(xScale)
.orient("bottom")
.tickPadding([5])
;
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10)
;
//Create SVG with the above specs
var svg = d3.select("#container")
.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("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
;
svg
.append("g")
.attr("class", "y axis")
.append("text") // just for the title (ticks are automatic)
.attr("transform", "rotate(-90)") // rotate the text!
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("frequency")
;
var temp = svg
.append("g")
.attr("class", "domx")
;
d3.csv("data3.csv", function(error, csv_data) {
// Filter the dataset to only get dept_1
var new_data = csv_data.filter(function(d) {
return d['dept'] == 'dept_1';
});
// function to handle histogram.
function histoGram(new_data){
//Create Scales
xScale
.domain(new_data.map(function(d) {return d.Pos;}))
;
yScale
// .domain([0, d3.max(new_data, function(d) { return d.Value; })])
.domain([0, d3.max(new_data, function(d) { return d.Value; })])
// .domain([0, 20])
;
svg
.select(".x.axis")
.transition()
.duration(1500)
.call(xAxis)
;
svg
.select(".y.axis")
.transition()
.duration(1500)
.call(yAxis)
;
// Data Join
var MyGroups = temp
.selectAll("g")
.data(new_data);
;
var MyGroupsEnter = MyGroups
.enter()
.append("g")
;
//Update
MyGroups
.attr("class", "update")
;
//Enter
MyGroupsEnter
.append("rect")
.attr("class", "enter")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("y", function(d) { return (yScale(d.Value));})
.attr("width", xScale.rangeBand())
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.attr("fill", function(d) {return "rgb(0, 0, 0)";})
.style("fill-opacity", 0.2)
;
MyGroupsEnter
.append("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 10; })
;
//Enter + Update
MyGroups
.transition()
.duration(1500)
.select("rect")
.attr("x", function(d) { return xScale(d.Pos); })
.attr("width", xScale.rangeBand())
.attr("y", function(d) { return (yScale(d.Value));})
.attr("height", function(d) { return (height - yScale(d.Value)); })
.text(function(d) { return d.Value; })
.style("fill-opacity", 1) // set the fill opacity
.attr("fill", function(d) {return "rgb(0, 0, " + (d.Value * 30) + ")";})
;
MyGroups
.transition()
.duration(1500)
.select("text")
.attr("class", "text")
.text(function(d) { return d.Value; })
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "black")
.attr("text-anchor", "middle")
.attr("x", function(d) { return xScale(d.Pos) + xScale.rangeBand()/2; })
.attr("y", function(d) { return yScale(d.Value) - 8; })
;
MyGroups
.exit()
.transition()
.duration(1500)
.remove()
;
}
histoGram(new_data);
var options = ["dept_1","dept_2","dept_3"];
var dropDown = d3
.select("#sel_button")
.append("select")
.attr("name", "options-list")
.attr("id", "id-name");
var options = dropDown
.selectAll("option")
.data(options)
.enter()
.append("option");
options
.text(function (d) { return d; })
.attr("value", function (d) { return d; });
d3.select("#id-name")
.on("change", function() {
var value = d3.select(this).property("value");
var new_data2 = csv_data.filter(function(d) {
return d['dept'] == value;
});
histoGram(new_data2);
});
});
Here is the data:
dept,Pos,Value
dept_1,d1_p1,1
dept_1,d1_p10,10
dept_1,d1_p11,11
dept_1,d1_p12,12
dept_2,d2_p1,1.5
dept_2,d2_p2,3
dept_2,d2_p3,4.5
dept_2,d2_p4,6
dept_2,d2_p5,7.5
dept_2,d2_p6,9
dept_2,d2_p7,10.5
dept_2,d2_p8,12
dept_2,d2_p9,13.5
dept_2,d2_p10,15
dept_2,d2_p11,16.5
dept_2,d2_p12,17.5
dept_2,d2_p13,18.5
dept_3,d3_p1,5
dept_3,d3_p2,7
dept_3,d3_p3,10
Firgured out what was my problem. I hadn't defined the format of the values. The max function was returning the maximum number out of character values (9). I added the following piece of code prior to the domain function and everything now works fines.
csv_data.forEach(function(d) {
d.dept = d.dept;
d.Pos = d.Pos;
d.Value = +d.Value;
});

how to align y axis label in D3

i am trying to create a stacked bar graph.
however i can't seem to get the alignment right.the graph has two axis.
because of the length of the y axis label it is partially blocked.
i tried solving this by using different CSS styles on the label and on the enclosing div,
but they did not have the desired affect.
i created a jsfidel to explain my case.
http://jsfiddle.net/2khbceut/1/
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 167772376>>167772375"}];
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: 65},
width = 1000 - margin.left - margin.right,
height = 1000 - 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)");
// });
}
i will appreciate any insight you have on this matter.

Categories