Reputation: 252
I have a focus and context bar graph developed using d3.js which works perfectly fine but when I zoom out the area, I want to show the grouping value of the graph.
As seen in the below screenshot, there are two bars of value 1 in it but when I zoom out to show data it for a month, it shows only one bar with value 1 on it.
I want to group that data so it would show two when it is zoomed out. Any help is appreciated.
I have two files: 1. Base file:
defaults: {
margin: {top: 10, right: 20, bottom: 100, left: 40},
margin2: {top: 425, right: 20, bottom: 30, left: 300},
},
onRender: function() {
var that = this;
//Set up graph parameters
var margin = this.options.margin;
var margin2 = this.options.margin2;
this.height = (this.options.height)? this.options.height - margin.top - margin.bottom: 960 - margin.top - margin.bottom,
this.width = (this.options.width)? this.options.width - margin.left - margin.right: 500 - margin.left - margin.right,
this.height2 = (this.options.height)? this.options.height - margin2.top - margin2.bottom : 500 - margin2.top - margin2.bottom,
this.width2 = this.width * .5;
//Set up ranges (a scaling factor to map input data to output in pixels)
this.scales = {
x: this.getXScale(),
x2: this.getX2Scale(),
y: this.getYScale(),
y2: this.getY2Scale()
};
//Set up and define graph content
//----------axis----------
this.renderAxes();
//Setup groups to organize layout, brush areas and perform clipping
//----------groups----------
this.svg = d3.select(this.el).append("svg")
.attr("class","FCBChart")
.attr("width", this.width + margin.left + margin.right)
.attr("height", this.height + margin.top + margin.bottom)
.attr("viewBox", "0 0 " + (this.width + margin.left + margin.right) + " " + (this.height + margin.top + margin.bottom) )
.attr("preserveAspectRatio", "xMidYMid meet");
this.svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", this.width + margin.left + margin.right)
.attr("height", this.height + margin.top + margin.bottom);
var aspect = (this.width + margin.left + margin.right) / (this.height + margin.top + margin.bottom);
$(window).on("resize", function() {
var targetWidth = $(".FCBChart").parent().width();
$(".FCBChart").attr("width", targetWidth);
$(".FCBChart").attr("height", targetWidth / aspect);
});
this.focus = this.svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class","focusGraph");
// barsGroup is for making FCB graph like Bar graph
this.barsGroup = this.focus.append("g")
.attr('clip-path', 'url(#clip)');
this.context = this.svg.append("g")
.attr("class","contextGraph")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
this.setupBrush();
this.renderData();
return this;
}
2.Actual file with functions:
initialize : function(option){
this.options = $.extend(true, {}, option,this.defaults);
},
events : {
},
//Set up ranges (a scaling factor to map input data to output in pixels)
getXScale : function(){
return d3.time.scale().range([0, this.width])
},
getX2Scale : function(){
return d3.time.scale().range([0, this.width2])
},
getYScale : function(){
return d3.scale.linear().range([this.height, 0])
},
getY2Scale : function(){
return d3.scale.linear().range([this.height2, 0])
},
//Set up and define graph content
//----------axis----------
renderAxes : function(){
var that = this;
this.xAxis = d3.svg.axis().scale(this.scales.x).orient("bottom"),
this.xAxis2 = d3.svg.axis().scale(this.scales.x2).orient("bottom"),
this.yAxis = d3.svg.axis().scale(this.scales.y).orient("left");
//----------area fill----------
this.area = d3.svg.area()
.x(function(d) {
var that1 = that;
return that1.scales.x(d.x); })
.y0(this.height)
.y1(function(d) {
var that1 = that;
return that1.scales.y(d.y); });
},
//----------Setup brush-----------------------
setupBrush : function(){
var that = this;
this.brush = d3.svg.brush()
.x(this.scales.x2)
.on("brush", function(){
that.brushed(this,that);
});
},
brushed : function(d3This, view) {
var that = view;
that.scales.x.domain(that.brush.empty() ? that.scales.x2.domain() : that.brush.extent());
//For FCB with bar chart
that.focusGraph.attr("x", function(d, i) { return that.scales.x(d.XPoint); });
that.focusGraph.attr("width", 20);
that.focus.select(".x.axis").call(that.xAxis);
},
renderData : function(){
var that = this;
var x = this.scales.x,
y = this.scales.y,
x2 = this.scales.x2,
y2 = this.scales.y2,
data = this.options.data;
data.forEach(function(d) {
d.XPoint = d.x;
d.values = +d.y;
});
// Scale the range of the data
x.domain(d3.extent(data, (function(d) { return d.XPoint; })));
y.domain([0, d3.max(data, (function(d) { return d.values; }))]);
// Scale the range of the data in context graph too
x2.domain(x.domain());
y2.domain(y.domain());
// Add the area graph to focus and context
//To get bar chart in FCB
this.focusGraph = this.barsGroup.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x(d.XPoint); })
.attr("y", function(d) { return y(d.values); })
.attr("width", 5)
.attr("height", function(d) { return that.height - y(d.values); })
.style("fill", "steelblue");
this.context.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return x2(d.XPoint); })
.attr("y", function(d) { return y2(d.values); })
.attr("width", 5)
.attr("height", function(d) { return that.height2 - y2(d.values); })
.style("fill", "steelblue");
//Functions called after all json datasets loaded
var x0 = x.copy();
//Axis
this.focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.height + ")")
.call(this.xAxis);
this.focus.append("g")
.attr("class", "y axis")
.call(this.yAxis);
this.context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + this.height2 + ")")
.call(this.xAxis2);
this.context.append("g")
.attr("class", "x brush")
.attr("clip-path", "url(#clip)")
.call(this.brush)
.selectAll("rect")
.attr("y", 0)
.attr("height", this.height2);
this.context.append("text")
.attr("class","instructions")
.attr('x',"345")
.attr('y','70')
// .attr("transform", "translate(0," + (this.height2 + 25) + ")")
.text('*Click and drag above to zoom / pan the data');
},
Data which i provide to graph is using an API call, which for the shown image is as shown below:
As we can see, on 28th Feb there are total 3 counts but when i zoom out the graph and want to analyse the week's data (as shown in below image), it still shows me count on 28th Feb as 1, where in fact it should show 3.
Upvotes: 2
Views: 708
Reputation: 7687
My guess is that what's happening is that you are rendering two bars even when zoomed out. They're either directly on top of each other or else very close to it. What you probably want is something called binning. Binning is where you take your data and bin it based on frequency of appearance in your data.
To implement this, you can add a simple binning function and call it every time you zoom. Assuming that you adjust your scale to match your zoomed domain, and that you want to divide your data into 20 bins, It would look something like this:
var myDomain = zoomedScale.domain(),
binSize = (myDomain[1] - myDomain[0]) / 20,
bins = [],
binIndex;
for(var i = 0; i < 20; i++){
bins.push(0);
}
for(var i = 0; i < myData.length; i++){
binIndex = Math.floor((myData[i].value - myDomain[0])/binSize);
if(binIndex === 20){
binIndex[19] += myData[i].value;
} else {
bins[binIndex] += myData[i].value;
}
}
Bins would now represent the sum of the data points that fall in each bin. You can then update your plot to plot the bins rather than the raw data.
Upvotes: 1