Wednesday, December 11, 2013

Pie is yummy.

While I am thinking about it I wanted to show off the code I wrote for the dojo piecharting widget I allude to here and here. It follows. A coworker sent out the link here earlier suggesting that pie charts are in fact a really stupid way to try to communicate data. After reading the article I am sold on its logic. It seems like the legend for a pie chart is the real chart and the colorful circle is just some gravy for it if you think about it. Sigh. That said I was in a car crash this evening and some pie sure does sound tasty right now.

define([
   "dojo/_base/lang",
   "ah/mdo",
   "ah/Util",
   "dojo/_base/declare",
   "dojo/_base/lang",
   "dojo/dom",
   "dojo/on",
   "dijit/_WidgetBase",
   "dijit/_TemplatedMixin",
   "dojo/text!ah/widgets/PieChart.html",
   "lib/underscore",
   "dojo/dom-style",
   "dojo/dom-class",
   "dojo/dom-construct",
   "dojox/charting/Chart",
   "dojox/charting/themes/Distinctive",
   "dojox/charting/plot2d/Pie",
   "dojox/charting/DataChart",
   "dojox/charting/widget/Legend"
], function(lang, mdo, Util, declare, lang, dom, on, _WidgetBase, _TemplatedMixin,
      template, _, domStyle, domClass, domConstruct, Chart, defaultTheme,
      Pie, DataChart, Legend){
   
   var app;
   var legend;
   var pieChart;
   var pieChartData;
   var legendData;
   var error;
   
   return declare("ah.widgets.PieChart", [_WidgetBase, _TemplatedMixin], {
      templateString: template,
      
      init: function() {
         app = this.app;
      },
      
      postCreate: function() {
      },
      
      setData: function(data) {
         pieChartData = data;
         legendData = new Array();
         var totalRecords = 0;
         for (i = 0; i < data.length; i++) {
            totalRecords = totalRecords + data[i].y;
         }
         for (i = 0; i < data.length; i++) {
            var number = data[i].y;
            var description = data[i].text;
            var percentage = "-";
            if (totalRecords > 0) {
               var piece = (number/totalRecords)*100;
               if (piece >= 10) {
                  percentage = piece.toPrecision(3) + "%";
               } else {
                  percentage = piece.toPrecision(2) + "%";
               }
            }
            legendData.push({ number: number, percentage: percentage, description:
                  description })
         }
      },
      
      prepChart: function(theme) {
         if(pieChartData) {
            if(pieChart) {
               pieChart.removeSeries("series");
            } else {
               pieChart = new Chart(this.chartNode);
            }
            if (theme) {
               pieChart.setTheme(theme);
            } else {
               pieChart.setTheme(defaultTheme);
            }
            pieChart.addPlot("default", { type: Pie });
            pieChart.addSeries("series",pieChartData);
            pieChart.surface.rawNode.setAttribute('width', domStyle.get(this.chartWrapper,
                  "width"));
            pieChart.surface.rawNode.setAttribute('height', domStyle.get(this.chartWrapper,
                  "height"));
            pieChart.render();
            pieChart.surface.rawNode.childNodes[1].setAttribute('stroke-opacity','0');
            pieChart.surface.rawNode.childNodes[1].setAttribute('fill-opacity','0');
            pieChart.surface.rawNode.childNodes[2].setAttribute('stroke-opacity','0');
            pieChart.surface.rawNode.childNodes[2].setAttribute('fill-opacity','0');
            pieChart.surface.rawNode.childNodes[3].setAttribute('stroke-opacity','0');
            pieChart.surface.rawNode.childNodes[3].setAttribute('fill-opacity','0');
            Legend({chart:pieChart}, this.legend);
            legend = dom.byId("dojox_charting_widget_Legend_0");
            if (legend) {
               domClass.add(legend, "display-none");
            }
         } else {
            error = "The call to prepChart does not have a preceding call to setData and thus
                  is moot.";
            console.log(error);
         }
      },
      
      prepLegend: function(table) {
         if (pieChartData) {
         while (table.hasChildNodes()) {
            table.removeChild(table.lastChild);
         }
         var header = domConstruct.create("tr", {}, table)
         domConstruct.create("td", { innerHTML: "quantity & percentage", colspan: 4, class:
               "right-padding" }, header);
         
         if (legend) {
            var tableRows = legend.childNodes[0].childNodes[0].childNodes;
         }
         
         for (i = 0; i < legendData.length; i++) {
            var row = domConstruct.create("tr", {}, table);
            var legendKey = "";
            if (legend) {
               legendKey =
                     legend.childNodes[0].childNodes[0].childNodes[i].childNodes[0].innerHTML;
               }
               domConstruct.create("td", { innerHTML: legendData[i].number, class:
                     "data-column" }, row);
               domConstruct.create("td", { innerHTML: legendData[i].percentage, class:
                     "data-column" }, row);
               domConstruct.create("td", { innerHTML: legendData[i].description, class:
                     "description-column" }, row);
               domConstruct.create("td", { innerHTML: legendKey }, row);
            }
         } else {
            error = "The call to prepLegend does not have a preceding call to setData and
                  thus is moot.";
            console.log(error);
         }
      },
      
      getLegendData: function() {
         return legendData;
      },
      
      getError: function() {
         pieChartData = null;
         return error;
      },
      
      getSvg: function() {
         return pieChart.surface.rawNode;
      }
   });
});

No comments:

Post a Comment