d3-scatterplot.js 6.59 KB
function D3ScatterPlot(placeholderSelector, data, config){
    this.utils = new D3ScatterPlotUtils();
    this.placeholderSelector = placeholderSelector;
    this.svg=null;
    this.defaultConfig = {
        width: 0,
        height: 0,
        margin:{
            left: 50,
            right: 30,
            top: 30,
            bottom: 50
        },
        x:{// X axis config
            label: 'X', // axis label
            value: function(d) { return d[0] }, // x value accessor
            orient: "bottom"
        },
        y:{// Y axis config
            label: 'Y', // axis label
            value: function(d) { return d[1] }, // y value accessor
            orient: "left"
        },
        dot:{
            radius: 2,
            color: function(d) { return d[0]*d[1] }, // string or function returning color's value for color scale
            d3ColorCategory: 'category10'
        }
    };

    if(data){
        this.setData(data);
    }

    if(config){
        this.setConfig(config);
    }

}

D3ScatterPlot.prototype.setData = function (data){
    this.data = data;
    return this;
};

D3ScatterPlot.prototype.setConfig = function (config){
    this.config = this.utils.deepExtend({}, this.defaultConfig, config);
    return this;
};
D3ScatterPlot.prototype.initPlot = function (){
    var self=this;
    var margin = this.config.margin;
    var conf = this.config;
    this.plot={
        x: {},
        y: {},
        dot: {
            color: null//color scale mapping function
        }
    };

    var width = conf.width;
    var placeholderNode = d3.select(this.placeholderSelector).node();

    if(!width){
        width =placeholderNode.getBoundingClientRect().width;
    }
    var height = conf.height;
    if(!height){
        height =placeholderNode.getBoundingClientRect().height;
    }

    this.plot.width = width - margin.left - margin.right;
    this.plot.height = height - margin.top - margin.bottom;

    this.setupX();
    this.setupY();

    if(conf.dot.d3ColorCategory){
        this.plot.dot.colorCategory = d3.scale[conf.dot.d3ColorCategory]();
    }
    var colorValue = conf.dot.color;
    if(colorValue){
        this.plot.dot.colorValue = colorValue;

        if (typeof colorValue === 'string' || colorValue instanceof String){
            this.plot.dot.color = colorValue;
        }else if(this.plot.dot.colorCategory){
            this.plot.dot.color = function(d){
                return self.plot.dot.colorCategory(self.plot.dot.colorValue(d));
            }
        }


    }

    return this;
};

D3ScatterPlot.prototype.setupX = function (){

    var plot = this.plot;
    var x = plot.x;
    var conf = this.config.x;

    /*
     * value accessor - returns the value to encode for a given data object.
     * scale - maps value to a visual display encoding, such as a pixel position.
     * map function - maps from data value to display value
     * axis - sets up axis
     */
    x.value = conf.value;
    x.scale = d3.scale.linear().range([0, plot.width]);
    x.map = function(d) { return x.scale(x.value(d));};
    x.axis = d3.svg.axis().scale(x.scale).orient(conf.orient);
    var data = this.data;
    plot.x.scale.domain([d3.min(data, plot.x.value)-1, d3.max(data, plot.x.value)+1]);


};

D3ScatterPlot.prototype.setupY = function (){

    var plot = this.plot;
    var y = plot.y;
    var conf = this.config.y;

    /*
     * value accessor - returns the value to encode for a given data object.
     * scale - maps value to a visual display encoding, such as a pixel position.
     * map function - maps from data value to display value
     * axis - sets up axis
     */
    y.value = conf.value;
    y.scale = d3.scale.linear().range([plot.height, 0]);
    y.map = function(d) { return y.scale(y.value(d));};
    y.axis = d3.svg.axis().scale(y.scale).orient(conf.orient);


    var data = this.data;
    plot.y.scale.domain([d3.min(data, plot.y.value)-1, d3.max(data, plot.y.value)+1]);
};

D3ScatterPlot.prototype.drawPlot = function (){
    this.drawAxisX();
    this.drawAxisY();
    this.drawDots();
};
D3ScatterPlot.prototype.drawAxisX = function (){
    var self = this;
    var plot = self.plot;
    var axisConf = this.config.x;
    self.svgG.append("g")
        .attr("class", "mw-axis mw-axis-x")
        .attr("transform", "translate(0," + plot.height + ")")
        .call(plot.x.axis)
        .append("text")
        .attr("class", "mw-label")
        .attr("transform", "translate("+ (plot.width/2) +","+ (self.config.margin.bottom) +")")  // text is drawn off the screen top left, move down and out and rotate
        .attr("dy", "-1em")
        .style("text-anchor", "middle")
        .text(axisConf.label);
};

D3ScatterPlot.prototype.drawAxisY = function (){
    var self = this;
    var plot = self.plot;
    var axisConf = this.config.y;
    self.svgG.append("g")
        .attr("class", "mw-axis mw-axis-y")
        .call(plot.y.axis)
        .append("text")
        .attr("class", "mw-label")
        .attr("transform", "translate("+ -self.config.margin.left +","+(plot.height/2)+")rotate(-90)")  // text is drawn off the screen top left, move down and out and rotate
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text(axisConf.label);
};


D3ScatterPlot.prototype.drawDots = function (){
    var self = this;
    var plot = self.plot;
    var data = this.data;
    var dots = self.svgG.selectAll(".mw-dot")
        .data(data)
        .enter().append("circle")
        .attr("class", "mw-dot")
        .attr("r", self.config.dot.radius)
        .attr("cx", plot.x.map)
        .attr("cy", plot.y.map);

    if(plot.dot.color){
        dots.style("fill", plot.dot.color)
    }

};

D3ScatterPlot.prototype.initSvg = function (){
    var self = this;
    var config = this.config;



    var width = self.plot.width+ config.margin.left + config.margin.right;
    var height =  self.plot.height+ config.margin.top + config.margin.bottom;
    var aspect = width / height;
    self.svg = d3.select(self.placeholderSelector).append("svg")
        .attr("width", width)
        .attr("height", height)
        .attr("viewBox", "0 0 "+" "+width+" "+height)
        .attr("preserveAspectRatio", "xMidYMid meet")
        .attr("class", "mw-d3-scatterplot");
    self.svgG = self.svg.append("g")
        .attr("transform", "translate(" + config.margin.left + "," + config.margin.top + ")");

    if(!config.width || config.height ){
        d3.select(window)
            .on("resize", function() {
                //TODO add responsiveness if width/height not specified
            });
    }

};

D3ScatterPlot.prototype.init = function (){
    var self = this;
    self.initPlot();
    self.initSvg();
    self.drawPlot();

};