Tags

, , , ,

For quite some time I have been playing with D3.js charts and visualizations. After some time, I managed to lay my hands on a nice example, a bar chart (horizontal bars).

From that example, I swapped the values of the x and y axis and created a working column chart. The next step was to make the following ‘object’ version, which is given below.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
        <title>Column Chart (Vertical Bar Chart)</title>
        <meta http-equiv="X-UA-Compatible" content="IE=9">
        <meta charset='utf-8'>
        <!--<script src="http://d3js.org/d3.v3.min.js"></script>-->
        <script src="js/d3.v3-3.3.6.js"></script>
</head>
<body>

<!--<div id="chart" style="width:1000px; height:400px;"></div>-->
<div id="chart" style="width:1000; height:500;"></div>

<script>
/* initial methods
function getElementWidth(elem)
{
        return parseInt(elem.style.width);
}

function getElementHeight(elem)
{
        return parseInt(elem.style.height);
}
*/

/* get style of element */
/* http://atomicrobotdesign.com/blog/javascript/get-the-style-property-of-an-element-using-javascript/ */
function getStyle(elem, prop)
{
  return window.getComputedStyle(elem, null).getPropertyValue(prop);
}

function getElementComputedStyleValue(elem, property, defaultValue)
{
        var retValue = defaultValue;
        var value = getStyle(elem, property);

        if ( value != null && value != "" && value == 0 ) {
                retValue = parseInt(value);
        }

        return retValue;
}

function ColumnChart(chartID)
{
        /* object variables */
        this.version = "1.0.0";
        this.id = chartID;

        /* object methods */
        this.draw = function(divID, dataFormat, dataSource, dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, xAxisDataColumnName, yAxisDataColumnName)
        {
                var data = null;
                if ( dataFormat == "csv" ) {
                        if ( dataSource == "text" ) {
                                data = d3.csv.parse(dataSet);
                        } else if ( dataSource == "url" ) {
                                data = dataSet;
                        }
                } else if ( dataFormat == "tsv" ) {
                        data = dataSet;
                } else if ( dataFormat == "json" ) {
                        data = dataSet;
                }

                if ( divID == null || divID == "") {
                        divID = "body";
                }

                /* get the width and height of the element, to set height and width of chart accordingly */
                var selectedElement = document.getElementById(divID);
                chartConfig.width = getElementComputedStyleValue(selectedElement, "width", chartConfig.width);
                chartConfig.height = getElementComputedStyleValue(selectedElement, "height", chartConfig.height);

                var chartWidth = chartConfig.width - chartConfig.paddingX;
                var chartHeight = chartConfig.height - chartConfig.paddingY;

                var defaultBarLabelHeight = 0.10;
                if ( chartConfig.barLabelHeight == null || chartConfig.barLabelHeight == -1 ) {
                        // if bar height not provided by user, taken as 10%
                        chartConfig.barLabelHeight = chartHeight * defaultBarLabelHeight;
                }

                var maxBarHeight = chartHeight - chartConfig.barLabelHeight;
                // width of one bar
                var barWidth = (chartWidth - (gridConfig.labelWidth + gridConfig.chartOffsetHoriz)) / data.length;

                // accessor functions
                var barLabel = function(d) { return d[xAxisDataColumnName]; };
                var barValue = function(d) { return parseFloat(d[yAxisDataColumnName]); };

                // scales
                var xScale = d3.scale.ordinal()
                        .domain(d3.range(0, data.length))
                        .rangeBands([0, data.length * barWidth]);
                var x = function(d, i) { return xScale(i); };
                var xText = function(d, i) { var out = x(d, i) + xScale.rangeBand() / 2; return out;};
                var y = d3.scale.linear()
                        .domain([0, d3.max(data, barValue)])
                        .range([chartHeight, 0]);

                // svg container element
                var chart = d3.select("#" + divID)
                        .append("svg")
                        .attr("width", chartConfig.width)
                        .attr("height", chartConfig.height);

                // grid line
                if ( chartConfig.showGridLines == true || chartConfig.showGridLineLabels == true ) {
                        // grid line container
                        var gridContainer = chart.append("g")
                                .attr("transform", "translate(" + gridConfig.labelWidth + "," + gridConfig.labelHeight + ")");

                        // grid line labels
                        if ( chartConfig.showGridLineLabels == true ) {
                                gridContainer.selectAll("text")
                                        .data(y.ticks(chartConfig.numTicks))
                                        .enter().append("text")
                                        .attr("x", gridConfig.lineLabelXPosition)
                                        .attr("y", y)
                                        .attr("text-anchor", gridConfig.lineLabelPosition)
                                        .attr("font-size", gridConfig.lineLabelFontSize)
                                        .style("fill", gridConfig.lineLabelFillColor)
                                        .style("stroke", gridConfig.lineLabelStrokeColor)
                                        .text(String);
                        }

                        // grid lines
                        if ( chartConfig.showGridLines == true ) {
                                gridContainer.selectAll("line")
                                        .data(y.ticks(chartConfig.numTicks))
                                        .enter().append("line")
                                        .attr("y1", y)
                                        .attr("y2", y)
                                        .attr("x1", 0)
                                        .attr("x2", xScale.rangeExtent()[1] + gridConfig.chartOffsetHoriz)
                                        .style("fill", gridConfig.lineFillColor)
                                        .style("stroke", gridConfig.lineStrokeColor);
                        }
                }

                // bars
                var barsContainer = chart.append("g")
                        .attr("transform", "translate(" + (chartConfig.yAxisLabelWidth + gridConfig.chartOffsetHoriz) + "," + (gridConfig.labelHeight + gridConfig.chartOffsetVert) + ")");
                barsContainer.selectAll("rect")
                        .data(data)
                        .enter().append("rect")
                        .attr("x", x)
                        .attr("width", xScale.rangeBand())
                        .attr("y", function(d) { return y(barValue(d)); })
                        .attr("height", function(d) { return chartHeight - y(barValue(d)); })
                        .style("stroke", barConfig.strokeColor)
                        .style("fill",
                                function(d, i) {
                                        if ( barConfig.fillColor == "single" ) {
                                                return barConfig.fillColorScale;
                                        } else if ( barConfig.fillColor == "range" ) {
                                                var idx = i % barConfig.fillColorScale.length;
                                                return barConfig.fillColorScale[idx];
                                        } else if ( barConfig.fillColor == "scale" ) {
                                                return barConfig.fillColorScale(i);
                                        }
                                }
                        );

                // bar value labels
                if ( chartConfig.showBarValueLabels == true ) {
                        barsContainer.selectAll("text")
                                .data(data)
                                .enter().append("text")
                                .attr("x", xText)
                                .attr("dy", barValueLabelConfig.yOffset)
                                .attr("dx", barValueLabelConfig.xOffset)
                                .attr("y", function(d) { return y(barValue(d)); })
                                .attr("text-anchor", barValueLabelConfig.position)
                                .attr("font-size", barValueLabelConfig.fontSize)
                                .style("fill", barValueLabelConfig.fillColor)
                                .style("stroke", barValueLabelConfig.strokeColor)
                                .text(function(d) { return d3.round(barValue(d), barValueLabelConfig.rounding); });
                }

                // bar labels
                var labelsContainer = chart.append("g")
                        .attr("transform", "translate(" + (chartConfig.yAxisLabelWidth - chartConfig.xAxisVertPadding + gridConfig.chartOffsetHoriz) + "," + (gridConfig.labelHeight + gridConfig.chartOffsetVert) + ")");
                labelsContainer.selectAll("text")
                        .data(data)
                        .enter()
                        .append("text")
                        .attr("x", xText)
                        .attr("dx", barLabelConfig.xOffset)
                        .attr("y", chartHeight + chartConfig.xAxisVertPadding + barLabelConfig.fontSize)
                        .attr("text-anchor", barLabelConfig.position)
                        .attr("font-size", barLabelConfig.fontSize)
                        .style("fill", barLabelConfig.fillColor)
                        .style("stroke", barLabelConfig.strokeColor)
                        .text(barLabel);

                // x-axis line
                if ( chartConfig.showXAxis == true ) {
                        barsContainer.append("line")
                                .attr("x1", -gridConfig.chartOffsetHoriz)
                                .attr("x2", xScale.rangeExtent()[1] + gridConfig.chartOffsetHoriz)
                                .attr("y1", chartHeight)
                                .attr("y2", chartHeight)
                                .style("fill", chartConfig.xAxisFillColor)
                                .style("stroke", chartConfig.xAxisStrokeColor);
                }

                // y-axis line
                if ( chartConfig.showYAxis == true ) {
                        barsContainer.append("line")
                                .attr("x1", -gridConfig.chartOffsetHoriz)
                                .attr("x2", -gridConfig.chartOffsetHoriz)
                                .attr("y1", 0)
                                .attr("y2", chartHeight)
                                .style("fill", chartConfig.yAxisFillColor)
                                .style("stroke", chartConfig.yAxisStrokeColor);
                }
        },
        this.draw_CSV_URL = function(chartObject, divID, dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName)
        {
                d3.csv(dataSet, function(data) {
                        chartObject.draw(divID, "csv", "url", data, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
                });
        },
        this.draw_TSV_URL = function(chartObject, divID, dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName)
        {
                d3.tsv(dataSet, function(data) {
                        chartObject.draw(divID, "tsv", "url", data, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
                });
        },
        this.draw_JSON_Variable = function(chartObject, divID, dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName)
        {
                chartObject.draw(divID, "json", "variable", dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        },
        this.draw_JSON_URL = function(chartObject, divID, dataSet, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName)
        {
                d3.json(dataSet, function(data) {
                        chartObject.draw(divID, "json", "url", data.values, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
                });
        }
}
</script>
<script id="csv" type="text/csv">Name,Population (mill),Average Life Expectancy,Area (1000 sq mi),Continent
Canada,33.9,80.7,3854.085,America
US,308.3,78.2,3784.191,America
Germany,82.3,79.4,137.847,Europe
Russia,141.9,65.5,6601.668,Europe
Mexico,108.4,76.06,758.449,America
Brazil,193.3,71.99,3287.612,America
Spain,46.9,80.9,195.365,Europe
France,65.4,80.98,244.339,Europe
China,1339,73,3705.407,Asia
Australia,22.4,81.2,2969.907,Australia
UK,62,79.4,93.800,Europe
Italy,60.3,80.5,116.346,Europe
India,1184,64.7,1236.085,Asia
Japan,127.4,82.6,145.920,Asia
Iceland,0.3,81.8,40.000,Europe
Portugal,10.6,78.1,35.560,Europe
South Africa,50,49.3,471.445,Africa
Egypt,78.9,71.3,387.000,Africa
Sweden,9.3,80.9,170.410,Europe
</script>
<script id="tsv" type="text/tsv">Name Population (mill)         Average Life Expectancy         Area (1000 sq mi)        Continent
Canada 33.9    80.7    3854.085        America
US      308.3   78.2    3784.191        America
Germany       82.3    79.4    137.847 Europe
Russia   141.9   65.5    6601.668        Europe
Mexico 108.4   76.06   758.449 America
Brazil   193.3   71.99   3287.612        America
Spain   46.9    80.9    195.365 Europe
France  65.4    80.98   244.339 Europe
China   1339     73       3705.407        Asia
Australia        22.4    81.2    2969.907        Australia
UK      62       79.4    93.800   Europe
Italy     60.3    80.5    116.346 Europe
India    1184     64.7    1236.085        Asia
Japan  127.4   82.6    145.920 Asia
Iceland 0.3      81.8    40.000   Europe
Portugal         10.6    78.1    35.560   Europe
South Africa     50       49.3    471.445 Africa
Egypt   78.9    71.3    387.000 Africa
Sweden         9.3      80.9    170.410 Europe
</script>
<script>
var values2 = [
        { "Name": "Canada", "Population (mill)": 33.9, "Average Life Expectancy": 80.7, "Area (1000 sq mi)": 3854.085, "Continent": "America" },
        { "Name": "US", "Population (mill)": 308.3, "Average Life Expectancy": 78.2, "Area (1000 sq mi)": 3784.191, "Continent": "America" },
        { "Name": "Germany", "Population (mill)": 82.3, "Average Life Expectancy": 79.4, "Area (1000 sq mi)": 137.847, "Continent": "Europe" },
        { "Name": "Russia", "Population (mill)": 141.9, "Average Life Expectancy": 65.5, "Area (1000 sq mi)": 6601.668, "Continent": "Europe" },
        { "Name": "Mexico", "Population (mill)": 108.4, "Average Life Expectancy": 76.06, "Area (1000 sq mi)": 758.449, "Continent": "America" },
        { "Name": "Brazil", "Population (mill)": 193.3, "Average Life Expectancy": 71.99, "Area (1000 sq mi)": 3287.612, "Continent": "America" },
        { "Name": "Spain", "Population (mill)": 46.9, "Average Life Expectancy": 80.9, "Area (1000 sq mi)": 195.365, "Continent": "Europe" },
        { "Name": "France", "Population (mill)": 65.4, "Average Life Expectancy": 80.98, "Area (1000 sq mi)": 244.339, "Continent": "Europe" },
        { "Name": "China", "Population (mill)": 1339, "Average Life Expectancy": 73, "Area (1000 sq mi)": 3705.407, "Continent": "Asia" },
        { "Name": "Australia", "Population (mill)": 22.4, "Average Life Expectancy": 81.2, "Area (1000 sq mi)": 2969.907, "Continent": "Australia" },
        { "Name": "UK", "Population (mill)": 62, "Average Life Expectancy": 79.4, "Area (1000 sq mi)": 93.800, "Continent": "Europe" },
        { "Name": "Italy", "Population (mill)": 60.3, "Average Life Expectancy": 80.5, "Area (1000 sq mi)": 116.346, "Continent": "Europe" },
        { "Name": "India", "Population (mill)": 1184, "Average Life Expectancy": 64.7, "Area (1000 sq mi)": 1236.085, "Continent": "Asia" },
        { "Name": "Japan", "Population (mill)": 127.4, "Average Life Expectancy": 82.6, "Area (1000 sq mi)": 145.920, "Continent": "Asia" },
        { "Name": "Iceland", "Population (mill)": 0.3, "Average Life Expectancy": 81.8, "Area (1000 sq mi)": 40.000, "Continent": "Europe" },
        { "Name": "Portugal", "Population (mill)": 10.6, "Average Life Expectancy": 78.1, "Area (1000 sq mi)": 35.560, "Continent": "Europe" },
        { "Name": "South Africa", "Population (mill)": 50, "Average Life Expectancy": 49.3, "Area (1000 sq mi)": 471.445, "Continent": "Africa" },
        { "Name": "Egypt", "Population (mill)": 78.9, "Average Life Expectancy": 71.3, "Area (1000 sq mi)": 387.000, "Continent": "Africa" },
        { "Name": "Sweden", "Population (mill)": 9.3, "Average Life Expectancy": 80.9, "Area (1000 sq mi)": 170.410, "Continent": "Europe" }
];

var chartConfig = {
                width: 1500,
                height: 500,
                paddingX: 50,
                paddingY: 50,
                numTicks: 10,
                xAxisVertPadding: 5, // padding between bar and bar labels
                showBarValueLabels: true,
                showGridLines: true,
                showGridLineLabels: true,
                showXAxis: true,
                xAxisFillColor: "none",
                xAxisStrokeColor: "#000000",
                showYAxis: true,
                yAxisFillColor: "none",
                yAxisStrokeColor: "#000000",
                yAxisLabelWidth: 50,
                barLabelHeight: 50
        };

var colorScale = d3.scale.category20();

var barConfig = {
                strokeColor: "white",
                fillColor: "scale", // "single", "range", "scale"
                //fillColorScale: "steelblue"
                //fillColorScale: ["steelblue", "red"]
                fillColorScale: colorScale
};
var barLabelConfig = {
                fontSize: 12,
                fillColor: "#000000",
                strokeColor: "none",
                position: "middle",
                xOffset: ".35em"
        };
var barValueLabelConfig = {
                fontSize: 12,
                fillColor: "#000000",
                strokeColor: "none",
                rounding: 2,
                position: "middle",
                xOffset: 0,
                yOffset: 0
        };
var gridConfig = {
                labelHeight: 10, // space reserved for gridline labels
                labelWidth: 50, // space reserved for gridline labels
                chartOffsetHoriz: 5, // horizontal space between start of grid and first bar
                chartOffsetVert: 0, // vertical space between start of grid and first bar
                lineFillColor: "none",
                lineStrokeColor: "#ccc",
                lineLabelFontSize: 12,
                lineLabelFillColor: "#000000",
                lineLabelStrokeColor: "none",
                lineLabelPosition: "end",
                lineLabelXPosition: -5
        };

var labelColumnName = "Name";
var dataColumnName = "Population (mill)";
var divID = "chart";

var cc = new ColumnChart("id");

var dataFormat = "csv"; // "tsv" / "json" / "csv"
var dataSource = "text"; // "url" / "text" / "variable" (only for json)
if ( dataFormat == "csv" ) {
        if ( dataSource == "text" ) {
                cc.draw(divID, "csv", "text", d3.select("#csv").text(), chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        } else if ( dataSource == "url" ) {
                cc.draw_CSV_URL(cc, divID, "data/bar-column-chart3.csv", chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        }
} else if ( dataFormat == "tsv" ) {
        if ( dataSource == "text" ) {
                // not working as expected
                //cc.draw(divID, "tsv", "text", d3.select("#tsv").text(), chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
                alert("'tsv - text' functionality not implemented");
        } else if ( dataSource == "url" ) {
                cc.draw_TSV_URL(cc, divID, "data/bar-column-chart3.tsv", chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        }
} else if ( dataFormat == "json" ) {
        if ( dataSource == "variable" ) {
                cc.draw_JSON_Variable(cc, divID, values2, chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        } else if ( dataSource == "url" ) {
                cc.draw_JSON_URL(cc, divID, "data/bar-column-chart3.json", chartConfig, barConfig, barLabelConfig, barValueLabelConfig, gridConfig, labelColumnName, dataColumnName);
        }
}
</script>
</body>
</html>

Please note that the original code and examples of D3.js are available using BSD license. Hence this code is also under BSD license.

Advertisements