Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

issue while using d3 brush in a line+bar chart (zoom in line+bar chart)

I am trying to use brush to zoom my custom chart in d3.js but its not behaving the way it should.Any idea what could be wrong here is js fiddle http://fiddle.jshell.net/saurabh_nitc10/od8gfsd3/9/

like in this fiddle http://fiddle.jshell.net/CjaD3/1/ after brushing the bars are going out of yaxis. any idea

what is wrong with the existing fiddle.Its not behaving the way it is supposed to do after zooming.please help.I just updated the fiddle

here is the plugin I have created.

(function($) {
$.dualAxis = {};
var xMapObject=[];
var svg = '';
var focus = '';
var tip = '';
function DualAxis(element, options) {
    this.$element = $(element);
    this.options = options;
    this.margin = {top: 20, right: 80, bottom: 30, left: 40},
    width = $(this.$element.selector).width() - this.margin.left - this.margin.right,
    height = $(this.$element.selector).height() - this.margin.top - this.margin.bottom;
    this.options.height = (this.options.height == null? height: this.options.height);
    this.options.width = (this.options.width == null? width: this.options.width);
    this.rangeMax = this.getMaxX().length*100;
    if (this.rangeMax > 14000) this.rangeMax = 14000;
    this.enabled = true;
}

DualAxis.prototype = {
    draw: function(){
        this.options.data.bar.forEach(function(d) {
            d.value = +d.value;
        });
        this.options.data.line.forEach(function(d) {
            d.value = +d.value;
        });
        tip = d3.tip()
          .attr('class', 'd3-tip')
          .html(function(d) { 
            var name= xMapObject[d.date];if(name == undefined){name=d.date;}
            var table = '<table class="table table-condensed">'
                        + '<thead>'
                        + '<tr><th colspan="2" style="text-align:center"class="city">'+name+'</th></tr>'
                        + '</thead>'
                        + '<tbody>'
                        + '<tr><td>Total Sales</td><td class="visits">'+d.value+'</td></tr>'
                        + '</tbody>'
                        + '</table>';
            return table; 
          })
          .style({border: '1px solid #fff', 'box-shadow': '1px 1px 4px rgba(0,0,0,0.5)', 'border-radius': 'none','background':'#fff','color':'#555'})
          .offset([-12, 0]);
        // if(this.options.data.events.length > 1){
            // xOffset = (this.options.width/this.options.data.events.length) + 40;
        // } else {
            // xOffset = this.options.width/2;
        // }
        xOffset = 62;
        svg = this.getSvg();
        svg == null ? svg = d3.select(this.$element.selector).append("svg"): svg;
        svg = svg
        .attr("height", this.options.height+this.margin.bottom+this.margin.top)
        .attr("width",this.options.width+this.margin.right+this.margin.left)
        .attr('class',this.options.svgClassName);
        svg.append("defs").append("clipPath")
            .attr("id", "clip")
            .append("rect")
            .attr("width", this.options.width)
            .attr("height", this.options.height);
        focus = svg.append("g").attr("class", "focus").attr("transform", "translate(" + xOffset + "," + 40 + ")");
        svg.call(tip);
        //this.drawBackground(focus);

        this.drawRect(focus,tip);
        this.drawXAxis(focus);
        this.drawY1Axis(focus);
        this.drawY2Axis(focus);
        this.drawLine(focus);
        this.drawLineLow(focus);
        this.drawLineMedium(focus);
        this.drawLineHigh(focus);
        if (this.options.showLegend)this.drawLegend(focus);
        this.zoomBehaviour(focus);
    },
    drawRect: function(svg,tip){
        x = this.getX();
        x2 = this.getX();
        y1 = this.getY1();
        y2 = this.getY2();
        height = this.options.height - (this.options.height / 3);
        transTime = 0;
        if(this.options.animate) transTime = 1000;

        svg.selectAll("rect.bar").data(this.options.data.bar).enter().append("rect").attr("class","bar").attr("width", this.options.width/this.options.data.bar.length).attr("x", function (d) {
            return x(d.date);
        }).attr("y", height).attr("height", 0).style("fill", function (d,i) {
            return "#89A54E";
        }).on('mouseover', tip.show)
        .on('mouseout', tip.hide).transition().duration(transTime).attr("y", function (d) {
            if(isNaN(y1(d.value))) return 0;return y1(d.value);
        }).attr("height", function (d) {
             if(isNaN(y1(d.value))) return 0; return (height - y1(d.value));
        }).style("fill", function(d) { return "#89A54E";}).attr("rx","1.5").attr("ry","1.5");
    },

    drawXAxis:function(svg){
        svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(this.getXAxis()).append("text").attr("transform", "translate(" + xOffset + "," + 40 + ")").style("text-anchor", "end").text(this.options.xAxisText).style("font-weight", "bold");
    },
    drawY1Axis:function(svg){

        svg.append("g").attr("class", "y axis").call(this.getY1Axis()).append("text").attr('id','y1AxisText').attr("transform", "rotate(-90)").attr("y",-36 ).attr("x", -(this.options.height/5)).style("text-anchor", "end").style("fill", "#266866").style("font-weight", "bold").style("letter-spacing", "1px").text(this.options.y1AxisText);
    },
    drawY2Axis:function(svg){
        svg.append("g").attr("class", "y axis").call(this.getY2Axis()).attr("transform", "translate(" + this.options.width + " ,0)")    .append("text").attr('id','y2AxisText').attr("transform", "rotate(-90)").attr("y",47 ).attr("x", -(this.options.height/5)).style("text-anchor", "end").style("fill", "#266866").style("font-weight", "bold").style("letter-spacing", "1px").text(this.options.y2AxisText);
    },
    drawLegend: function(svg){
        legend = svg.append("g").attr("class", "legend").attr('transform', 'translate(-30,'+(height+50)+')');
        max = d3.max(this.options.values,function(d){return d.length;});
        legend.selectAll('rect').data(this.options.values).enter().append("rect").attr("x", function(d,i){return (i*149);}).attr("y", "0").attr("width", 12).attr("height", 12).style("fill", function(d,i){return color(i);});
        legend.selectAll('text').data(this.options.values).enter().append("text").attr("x", function (d, i) {return (i*149+15);}).attr("y", "10").text(function (d) {return d;});
    },
    getSvg: function(){
        return this.options.svg;
    },
    getXAxis:function(){
        //var x = d3.scale.ordinal().rangeRoundBands([0, this.options.width], 0);
        return  d3.svg.axis().scale(x).tickFormat(d3.time.format("%b'%y")).orient("bottom");
        //return d3.svg.axis().scale(x).tickValues(x.domain().filter(function(d, i) {return !(i % 10); })).orient("bottom");
    },
    getY1Axis:function(){
        return d3.svg.axis().scale(y1).orient("left").tickFormat(d3.format(".2s"));
    },
    getY2Axis:function(){
        return d3.svg.axis().scale(y2).orient("right").tickFormat(d3.format(".2s"));
    },
    getMaxX: function(){ //for ordinal we dont know what to scale 

        return  this.options.data.dateForxAxis.map(function (d) {
            return d.date;});
    },
    getMaxY1: function(){
        return d3.max(this.options.data.bar, function (d) {return d.value;});
    },
    getMaxY2: function(){
        return d3.max(this.options.data.line, function (d) {return d.value;});
    },
    getX: function(){
        return d3.time.scale().range([0, this.options.width]).domain(d3.extent(this.options.data.dateForxAxis, function(d) { return d.date; }));
    },
    getY1: function(){
        return d3.scale.linear().range([this.options.height - (this.options.height / 3), 0]).domain([0, this.getMaxY1()]);
    },
    getY2: function(){
        return d3.scale.linear().range([this.options.height - (this.options.height / 3), 0]).domain([0, this.getMaxY2()]);
    },
    drawBackground: function(vis){
        vis.append("rect").attr("x", 0).attr("y", 0).attr("width", this.options.width ).attr("height", this.options.height - 50).style("fill", "grey").attr("transform", "translate(0,0)").style("opacity", "0").attr("class","background").attr("id", "background");
    },
    zoomBehaviour: function(vis){
        var that = this;
        //zoomBehaviour = d3.behavior.zoom().scaleExtent([1, 1]).on("zoom", zoom);
        brush = d3.svg.brush().x(x2).on("brush", brushed);
        width = this.options.width;
        vis.append("g").attr("class","x brush").call(brush).selectAll("rect").attr("y",-6).attr("height",this.options.height+7);
        function brushed(){
            x.domain(brush.empty() ? x2.domain() : brush.extent());
            vis.selectAll("rect.bar")
            .attr("transform", function(d) { return "translate(" + x(d.date) + ",0)"; })
             vis.select(".x.axis").call(that.getXAxis());
             //vis.select(".line").attr("d", that.getLine());
        }
    },
    drawLine:function(svg){
        svg.append('path').datum(this.options.data.line).attr("class", "line").attr("d", this.getLine());
    },
    getLine:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.value); });
    },
    drawLineLow:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineLow").attr("d", this.getLineLow()).style("stroke-dasharray", ("3, 3"));
    },
    getLineLow:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.low); });
    },
    drawLineMedium:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineMedium").attr("d", this.getLineMedium());
    },
    getLineMedium:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.medium); });
    },
    drawLineHigh:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineHigh").attr("d", this.getLineHigh()).style("stroke-dasharray", ("3, 3"));
    },
    getLineHigh:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.high); });
    },

};
$.fn.dualAxis = function(options){

    options = $.extend({}, $.fn.dualAxis.defaults, options);
    if(options.data === '' || options.data === null){
        var err = 'dualAxis Error: Data Attribute Cannot be Empty or Null.';
        (typeof(console) != 'undefined' && console.error) ? 
            console.error(err) : 
            alert(err);
    }
    function elementOptions(ele, options) {
        return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
    };
    function get(ele) {
        var gb = $.data(ele, 'gb');
        if (!gb) {
            gb = new DualAxis(ele,elementOptions(ele, options));
            $.data(ele, 'gb', gb);
        }
        return gb;
    }
    var gb = get(this);
    gb.draw();
};

$.fn.dualAxis.defaults = {
        svgClassName: "dualAxis",
        svg: null,
        xAxisText: 'x-axis',
        y1AxisText: 'y1-axis',
        y2AxisText: 'y2-axis',
        maxX: null,
        maxyY: null,
        height:null,
        width:null,
        animate: true,
        color: ["#3366cc", "#dc3912", "#ff9900", "#109618"],
        showLegend:true,
        data: '',
    };
})(jQuery);

here is the format of data

{
"bar": [
    {
        "date": "50_2012",
        "value": "88787"
    },
    {
        "date": "155_2012",
        "value": "50573"
    },
     {
        "date": "155_2013",
        "value": "5057"
    }
],
"dateForxAxis": [
    {
        "date": "45_2012"
    },
    {
        "date": "155_2012"
    },
    {
        "date": "260_2013"
    }
],
"future": [
    {
        "high": "87878",
        "medium": "55535",
        "low": "1212"
    },
    {
        "high": "187878",
        "medium": "255535",
        "low": "14212"
    }
],
"line": [
    {
        "date": "50_2012",
        "value": "8787"
    },
    {
        "date": "60_2012",
        "value": "47474"
    },
     {
        "date": "168_2012",
        "value": "49474"
    }
]
};

calling the plugin

$('#dualAxis').dualAxis({
            data: viewData1,
            xAxisText: 'Time',
            y1AxisText:'Transactions',
            y2AxisText:'Sales',
            animate:true,
            showLegend:false
        });

Please suggest what Am I doing wrong.when it is zooming it should zoom even for line bar and the three other lines.

like image 908
Saurabh Sinha Avatar asked Apr 09 '15 12:04

Saurabh Sinha


1 Answers

In the brushed method, you are translating the bars instead of setting a new x position (causing them to be translated based on their original x position outside the graph). Re-setting the x position in the brushed method will place the bars correctly when scaling:

vis.selectAll("rect.bar").attr("x", function (d) {
  return x(d.date);
});

A fork of your jsfiddle with this change at http://fiddle.jshell.net/brendaz/zr6kkgaa/

like image 83
brenzy Avatar answered Oct 26 '22 23:10

brenzy