(function() {
  angular.module("RadarChart", [])
    .directive("radar", radar)
    .directive("onReadFile", onReadFile)
    .controller("MainCtrl", MainCtrl);

  // controller function MainCtrl
  function MainCtrl($http) {
    var ctrl = this;
    init();


    // function init
    function init() {
      // initialize controller variables
      ctrl.examples = [
        "data_the_avengers",
        "data_plant_seasons",
        "data_car_ratings"
      ];
      ctrl.exampleSelected = ctrl.examples[0];
      ctrl.getData = getData;
      ctrl.selectExample = selectExample;

      // initialize controller functions
      ctrl.selectExample(ctrl.exampleSelected);
      ctrl.config = {
        w: 250,
        h: 250,
        facet: false,
        levels: 5,
        levelScale: 0.85,
        labelScale: 0.9,
        facetPaddingScale: 2.1,
        showLevels: true,
        showLevelsLabels: false,
        showAxesLabels: true,
        showAxes: true,
        showLegend: true,
        showVertices: true,
        showPolygons: true
      };
    }

    // function getData
    function getData($fileContent) {
      ctrl.csv = $fileContent;
    }

    // function selectExample
    function selectExample(item) {
      var file = item + ".csv";
      $http.get(file).success(function(data) {
        ctrl.csv = data;
      });
    }
  }

  // directive function sunburst
  function radar() {
    return {
      restrict: "E",
      scope: {
        csv: "=",
        config: "="
      },
      link: radarDraw
    };
  }


  // directive function onReadFile
  function onReadFile($parse) {
    return {
      restrict: "A",
      scope: false,
      link: function(scope, element, attrs) {
        var fn = $parse(attrs.onReadFile);
        element.on("change", function(onChangeEvent) {
          var reader = new FileReader();
          reader.onload = function(onLoadEvent) {
            scope.$apply(function() {
              fn(scope, {
                $fileContent: onLoadEvent.target.result
              });
            });
          };
          reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
        });
      }
    };
  }
})();
<!DOCTYPE html>
<html ng-app="RadarChart">

<head>
  <meta charset="utf-8">
  <title>D3 Radar Chart</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/css/bootstrap.min.css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css" />
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,600" />
  <link rel="stylesheet" href="style.css" />
</head>

<body class="container" ng-controller="MainCtrl as radar">

  <!-- header -->
  <header class="page-header">
    <h1>D3 Radar Chart</h1> 
    <p class="text-small">AngularJS application showcasing an interactive D3 radar chart (with facetting).</p>
  </header>


  <!-- main content -->
  <div class="main container">
    <h2>Visualization</h2>

    <!-- visualization -->
    <div class="visualization col-xs-7">
      <p>Select example:
        <select ng-options="example for example in radar.examples" ng-model="radar.exampleSelected" ng-change="radar.selectExample(radar.exampleSelected)"></select>
      </p>
      <div class="visualization">
        <radar csv="radar.csv" config="radar.config"></radar>
      </div>
    </div>


    <!-- configuration -->
    <div class="configuration col-xs-5">
      <form>
        <h3>Configuration Parameters</h3>
        <div class="form-group">
          <label>Width:</label>
          <input type="number" class="form-control-inline" step="50" ng-model="radar.config.w" />
          <label>Height:</label>
          <input type="number" class="form-control-inline" step="50" ng-model="radar.config.h" />
        </div>
        <div class="form-group">
          <label>Levels:</label>
          <input type="number" class="form-control-inline" step="1" ng-model="radar.config.levels" />
        </div>
        <div class="form-group">
          <label>Padding Scale:</label>
          <input type="number" class="form-control-inline" step="0.1" ng-model="radar.config.facetPaddingScale" />
        </div>
        <div class="form-group">
          <label>Label Scale:</label>
          <input type="number" class="form-control-inline" step="0.1" ng-model="radar.config.labelScale" />
        </div>
        <div class="form-group">
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.facet" /><span class="text-primary">Facet Plot</span></label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showLevels" />Levels</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showAxes" />Axes</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showVertices" />Vertices</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showPolygons" />Polygons</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showLegend" />Legend</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showLevelsLabels" />Levels Labels</label>
          <label class="checkbox"><input type="checkbox" ng-model="radar.config.showAxesLabels" />Axes Labels</label>
        </div>
        <p class="text-muted">(NOTE: Not all configuration options are shown, refer to <code>README.md</code> or <code>radarDraw.js</code> for more details)</p>
      </form>
    </div>
  </div>
  <hr />


  <!-- description -->
  <div class="description">
    <p>A radar chart visualizes multivariate data in a 2D chart of three or more quantitative variables represented on axes. Use the configuration parameters above to adjust the plot to your tastes, and you can also choose to view the plots stacked vs facetted.</p>
    <p>For custom testing, load up a file conforming to the data schema (see Data section below) or you can test out the following sample files:</p>
    <ul>
      <li><a href="data_the_avengers.csv" target="_blank">The Avengers</a> (Power Grid ratings)</li>
      <li><a href="data_plant_seasons.csv" target="_blank">Plant Seaons</a> (mock data)</li>
      <li><a href="data_car_ratings.csv" target="_blank">Car Ratings</a> (mock data)</li>
    </ul>
    <input id="fileUpload" type="file" on-read-file="radar.getData($fileContent)" />
  </div>
  <hr/>



  <!-- details -->
  <div class="Details">
    <h2>Details</h2>
    <p>This is a variation of the <a href="http://bl.ocks.org/tpreusse/2bc99d74a461b8c0acb1" target="_blank">original</a> and <a href="http://bl.ocks.org/nbremer/6506614" target="_blank">improved</a> D3 radar chart. Main D3 drawing logic is located in the <code>radar.js</code> file.</p>
    <p>Major improvements include:</p>
    <ul>
      <li>Refactoring D3 components (levels, labels, axes, polygons, legend), which now can be controlled through the <code>config</code> object (see configuration parameters).</li>
      <li>Abstracting the building and rendering portions of the D3 visualization.</li>
      <li>Aside from the basic stacked view, this variation includes a facetting option to plot the graphs in a facet grid.</li>
    </ul>
    <p>The data input takes the form of a csv file with the following schema:</p>
    <ul>
      <li><code>group (int/string):</code> data to be grouped into an object to plot the required polygon on the radar chart.</li>
      <li><code>axis (int/string):</code> the axis of the radar charts (dimensions of the multivariate data).</li>
      <li><code>value (int):</code> the data value of the given record.</li>
      <li><code>description (int/string):</code> not a mandatory field, and additional columns after this are accepted as well.</li>
    </ul>
    <hr />
  </div>


  <!-- data/file preview -->
  <div class="preview">
    <h2>Data</h2>
    <pre>{{ radar.csv }}</pre>
  </div>


  <!-- footer -->
  <footer>
    <p><a href="http://bl.ocks.org/chrisrzhou/2421ac6541b68c1680f8" target="_blank">D3 Radar Chart</a> by chrisrzhou, 2015-01-15
      <br />
      <a href="http://github.com/chrisrzhou" target="_blank"><i class="fa fa-github"></i></a> |
      <a href="http://bl.ocks.org/chrisrzhou" target="_blank"><i class="fa fa-th"></i></a> |
      <a href="http://www.linkedin.com/in/chrisrzhou" target="_blank"><i class="fa fa-linkedin"></i></a>
    </p>
  </footer>


  <!-- scripts -->
  <script src="http://code.angularjs.org/1.3.5/angular.js"></script>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <script src="app.js"></script>
  <script src="radar.js"></script>
  <script src="radarDraw.js"></script>
  <script>
    // Hack to make this example display correctly in an iframe on bl.ocks.org
    d3.select(self.frameElement).style("height", "1200px");
  </script>
</body>

</html>
body {
  font-family: "Open Sans", sans-serif;
  font-size: 12px;
  font-weight: 400;
  padding-top: 10px;
  padding-bottom: 100px;
}
html {
  overflow-y: scroll;
}
h1 {
  color: steelblue;
  font-weight: 800;
  font-size: 1.7em;
}
h2 {
  color: steelblue;
  font-size: 1.3em;
  padding-bottom: 10px;
}
h3 {
  color: gray;
  font-size: 1.2em;
  padding-bottom: 10px;
}
footer a,
footer a:hover, footer a:visited {
  color: #D2A000;
}
.text-small {
  font-size: 12px;
  font-style: italic;
}
footer {
  color: white;
  padding-top: 5px;
  border-top: 1px solid gray;
  font-size: 12px;
  position: fixed;
  left: 0;
  bottom: 0;
  height: 50px;
  width: 100%;
  background: black;
  text-align: center;
}
pre {
  height: 200px;
  font-size: 9px;
  overflow-y: scroll;
}
.form-control-inline {
  display: inline;
  width: 40px;
  margin-right: 5px;
}
.visualization {
}
.configuration {
  font-size: 0.8em;
  width: 250px;
  border: 1px solid #ddd;
  padding-left: 20px;
  margin-bottom: 20px;
}
.checkbox {
  margin-left: 20px;
}
/* Customizable classes used in D3 vis, uncomment to customize
.svg-vis {
  background-color: gray;
  opacity: 0.5;
}
.verticesTooltip {
  position: absolute;
  color: red;
  font-size: 12px;
  width: 100px;
  height: auto;
  padding: 5px;
  border: 2px solid gray;
  border-radius: 5px;
  pointer-events: none;
  opacity: 0;
  background: #f4f4f;
}
.level-lines {
  stroke: red;
  stroke-width: 1px;
}
.level-labels {
  fill: red;
  font-size: 12px;
}
.axis-lines {
  stroke: blue;
  stroke-width: 2px;
}
.axis-labels {
  fill: blue;
  font-size: 12px;
}
.polygon-vertices {
  fill-opacity: 0.6;
}
.polygon-areas {
  fill-opacity: 0.6;
}
.legend-tiles {
  fill-opacity: 0.3;
}
.legend-labels {
  font-size: 15px;
}
*/
/** RadarChart
 *
 * This is the main reuseable function to draw radar charts.
 *
 * The original d3 project is found on: https://github.com/alangrafu/radar-chart-d3
 * This version is based on the cleaned version found on: http://bl.ocks.org/nbremer/6506614
 * with some reorganization of code and added commenting, as well as further function abstractions
 * to allow for addition/removal of visualization components via tweaking configuration parameters.
 *
 **/





var RadarChart = {
  draw: function(id, data, options) {

    // add touch to mouseover and mouseout
    var over = "ontouchstart" in window ? "touchstart" : "mouseover";
    var out = "ontouchstart" in window ? "touchend" : "mouseout";

    /** Initiate default configuration parameters and vis object
     *
     **/
    // initiate default config
    var w = 300;
    var h = 300;
    var config = {
      w: w,
      h: h,
      facet: false,
      levels: 5,
      levelScale: 0.85,
      labelScale: 1.0,
      facetPaddingScale: 2.5,
      maxValue: 0,
      radians: 2 * Math.PI,
      polygonAreaOpacity: 0.3,
      polygonStrokeOpacity: 1,
      polygonPointSize: 4,
      legendBoxSize: 10,
      translateX: w / 4,
      translateY: h / 4,
      paddingX: w,
      paddingY: h,
      colors: d3.scale.category10(),
      showLevels: true,
      showLevelsLabels: true,
      showAxesLabels: true,
      showAxes: true,
      showLegend: true,
      showVertices: true,
      showPolygons: true
    };

    // initiate main vis component
    var vis = {
      svg: null,
      tooltip: null,
      levels: null,
      axis: null,
      vertices: null,
      legend: null,
      allAxis: null,
      total: null,
      radius: null
    };

    // feed user configuration options
    if ("undefined" !== typeof options) {
      for (var i in options) {
        if ("undefined" !== typeof options[i]) {
          config[i] = options[i];
        }
      }
    }

    render(data); // render the visualization





    /** helper functions
     *
     * @function: render: render the visualization
     * @function: updateConfig: update configuration parameters
     * @function: buildVis: build visualization using the other build helper functions
     * @function: buildVisComponents: build main vis components
     * @function: buildLevels: build "spiderweb" levels
     * @function: buildLevelsLabels: build out the levels labels
     * @function: buildAxes: builds out the axes
     * @function: buildAxesLabels: builds out the axes labels
     * @function: buildCoordinates: builds [x, y] coordinates of polygon vertices.
     * @function: buildPolygons: builds out the polygon areas of the dataset
     * @function: buildVertices: builds out the polygon vertices of the dataset
     * @function: buildLegend:  builds out the legend
     **/
    // render the visualization
    function render(data) {
      // remove existing svg if exists
      d3.select(id).selectAll("svg").remove();
      updateConfig();
      
      if (config.facet) {
        data.forEach(function(d, i) {
          buildVis([d]); // build svg for each data group

          // override colors
          vis.svg.selectAll(".polygon-areas")
            .attr("stroke", config.colors(i))
            .attr("fill", config.colors(i));
          vis.svg.selectAll(".polygon-vertices")
            .attr("fill", config.colors(i));
          vis.svg.selectAll(".legend-tiles")
            .attr("fill", config.colors(i));
        });
      } else {
        buildVis(data); // build svg
      }
    }


    // update configuration parameters
    function updateConfig() {
      // adjust config parameters
      config.maxValue = Math.max(config.maxValue, d3.max(data, function(d) {
        return d3.max(d.axes, function(o) { return o.value; });
      }));
      config.w *= config.levelScale;
      config.h *= config.levelScale;
      config.paddingX = config.w * config.levelScale;
      config.paddingY = config.h * config.levelScale;


      // if facet required:
      if (config.facet) {
        config.w /= data.length;
        config.h /= data.length;
        config.paddingX /= (data.length / config.facetPaddingScale);
        config.paddingY /= (data.length / config.facetPaddingScale);
        config.polygonPointSize *= Math.pow(0.9, data.length);
      }
    }


    //build visualization using the other build helper functions
    function buildVis(data) {
      buildVisComponents();
      buildCoordinates(data);
      if (config.showLevels) buildLevels();
      if (config.showLevelsLabels) buildLevelsLabels();
      if (config.showAxes) buildAxes();
      if (config.showAxesLabels) buildAxesLabels();
      if (config.showLegend) buildLegend(data);
      if (config.showVertices) buildVertices(data);
      if (config.showPolygons) buildPolygons(data);
    }

    // build main vis components
    function buildVisComponents() {
      // update vis parameters
      vis.allAxis = data[0].axes.map(function(i, j) { return i.axis; });
      vis.totalAxes = vis.allAxis.length;
      vis.radius = Math.min(config.w / 2, config.h / 2);

      // create main vis svg
      vis.svg = d3.select(id)
        .append("svg").classed("svg-vis", true)
        .attr("width", config.w + config.paddingX)
        .attr("height", config.h + config.paddingY)
        .append("svg:g")
        .attr("transform", "translate(" + config.translateX + "," + config.translateY + ")");;

      // create verticesTooltip
      vis.verticesTooltip = d3.select("body")
        .append("div").classed("verticesTooltip", true)
        .attr("opacity", 0)
        .style({
          "position": "absolute",
          "color": "black",
          "font-size": "10px",
          "width": "100px",
          "height": "auto",
          "padding": "5px",
          "border": "2px solid gray",
          "border-radius": "5px",
          "pointer-events": "none",
          "opacity": "0",
          "background": "#f4f4f4"
        });


      // create levels
      vis.levels = vis.svg.selectAll(".levels")
        .append("svg:g").classed("levels", true);

      // create axes
      vis.axes = vis.svg.selectAll(".axes")
        .append("svg:g").classed("axes", true);

      // create vertices
      vis.vertices = vis.svg.selectAll(".vertices");

      //Initiate Legend	
      vis.legend = vis.svg.append("svg:g").classed("legend", true)
        .attr("height", config.h / 2)
        .attr("width", config.w / 2)
        .attr("transform", "translate(" + 0 + ", " + 1.1 * config.h + ")");
    }


    // builds out the levels of the spiderweb
    function buildLevels() {
      for (var level = 0; level < config.levels; level++) {
        var levelFactor = vis.radius * ((level + 1) / config.levels);

        // build level-lines
        vis.levels
          .data(vis.allAxis).enter()
          .append("svg:line").classed("level-lines", true)
          .attr("x1", function(d, i) { return levelFactor * (1 - Math.sin(i * config.radians / vis.totalAxes)); })
          .attr("y1", function(d, i) { return levelFactor * (1 - Math.cos(i * config.radians / vis.totalAxes)); })
          .attr("x2", function(d, i) { return levelFactor * (1 - Math.sin((i + 1) * config.radians / vis.totalAxes)); })
          .attr("y2", function(d, i) { return levelFactor * (1 - Math.cos((i + 1) * config.radians / vis.totalAxes)); })
          .attr("transform", "translate(" + (config.w / 2 - levelFactor) + ", " + (config.h / 2 - levelFactor) + ")")
          .attr("stroke", "gray")
          .attr("stroke-width", "0.5px");
      }
    }


    // builds out the levels labels
    function buildLevelsLabels() {
      for (var level = 0; level < config.levels; level++) {
        var levelFactor = vis.radius * ((level + 1) / config.levels);

        // build level-labels
        vis.levels
          .data([1]).enter()
          .append("svg:text").classed("level-labels", true)
          .text((config.maxValue * (level + 1) / config.levels).toFixed(2))
          .attr("x", function(d) { return levelFactor * (1 - Math.sin(0)); })
          .attr("y", function(d) { return levelFactor * (1 - Math.cos(0)); })
          .attr("transform", "translate(" + (config.w / 2 - levelFactor + 5) + ", " + (config.h / 2 - levelFactor) + ")")
          .attr("fill", "gray")
          .attr("font-family", "sans-serif")
          .attr("font-size", 10 * config.labelScale + "px");
      }
    }


    // builds out the axes
    function buildAxes() {
      vis.axes
        .data(vis.allAxis).enter()
        .append("svg:line").classed("axis-lines", true)
        .attr("x1", config.w / 2)
        .attr("y1", config.h / 2)
        .attr("x2", function(d, i) { return config.w / 2 * (1 - Math.sin(i * config.radians / vis.totalAxes)); })
        .attr("y2", function(d, i) { return config.h / 2 * (1 - Math.cos(i * config.radians / vis.totalAxes)); })
        .attr("stroke", "grey")
        .attr("stroke-width", "1px");
    }


    // builds out the axes labels
    function buildAxesLabels() {
      vis.axes
        .data(vis.allAxis).enter()
        .append("svg:text").classed("axis-labels", true)
        .text(function(d) { return d; })
        .attr("text-anchor", "middle")
        .attr("x", function(d, i) { return config.w / 2 * (1 - 1.3 * Math.sin(i * config.radians / vis.totalAxes)); })
        .attr("y", function(d, i) { return config.h / 2 * (1 - 1.1 * Math.cos(i * config.radians / vis.totalAxes)); })
        .attr("font-family", "sans-serif")
        .attr("font-size", 11 * config.labelScale + "px");
    }


    // builds [x, y] coordinates of polygon vertices.
    function buildCoordinates(data) {
      data.forEach(function(group) {
        group.axes.forEach(function(d, i) {
          d.coordinates = { // [x, y] coordinates
            x: config.w / 2 * (1 - (parseFloat(Math.max(d.value, 0)) / config.maxValue) * Math.sin(i * config.radians / vis.totalAxes)),
            y: config.h / 2 * (1 - (parseFloat(Math.max(d.value, 0)) / config.maxValue) * Math.cos(i * config.radians / vis.totalAxes))
          };
        });
      });
    }


    // builds out the polygon vertices of the dataset
    function buildVertices(data) {
      data.forEach(function(group, g) {
        vis.vertices
          .data(group.axes).enter()
          .append("svg:circle").classed("polygon-vertices", true)
          .attr("r", config.polygonPointSize)
          .attr("cx", function(d, i) { return d.coordinates.x; })
          .attr("cy", function(d, i) { return d.coordinates.y; })
          .attr("fill", config.colors(g))
          .on(over, verticesTooltipShow)
          .on(out, verticesTooltipHide);
      });
    }


    // builds out the polygon areas of the dataset
    function buildPolygons(data) {
      vis.vertices
        .data(data).enter()
        .append("svg:polygon").classed("polygon-areas", true)
        .attr("points", function(group) { // build verticesString for each group
          var verticesString = "";
          group.axes.forEach(function(d) { verticesString += d.coordinates.x + "," + d.coordinates.y + " "; });
          return verticesString;
        })
        .attr("stroke-width", "2px")
        .attr("stroke", function(d, i) { return config.colors(i); })
        .attr("fill", function(d, i) { return config.colors(i); })
        .attr("fill-opacity", config.polygonAreaOpacity)
        .attr("stroke-opacity", config.polygonStrokeOpacity)
        .on(over, function(d) {
          vis.svg.selectAll(".polygon-areas") // fade all other polygons out
          .transition(250)
            .attr("fill-opacity", 0.1)
            .attr("stroke-opacity", 0.1);
          d3.select(this) // focus on active polygon
          .transition(250)
            .attr("fill-opacity", 0.7)
            .attr("stroke-opacity", config.polygonStrokeOpacity);
        })
        .on(out, function() {
          d3.selectAll(".polygon-areas")
            .transition(250)
            .attr("fill-opacity", config.polygonAreaOpacity)
            .attr("stroke-opacity", 1);
        });
    }


    // builds out the legend
    function buildLegend(data) {
      //Create legend squares
      vis.legend.selectAll(".legend-tiles")
        .data(data).enter()
        .append("svg:rect").classed("legend-tiles", true)
        .attr("x", config.w - config.paddingX / 2)
        .attr("y", function(d, i) { return i * 2 * config.legendBoxSize; })
        .attr("width", config.legendBoxSize)
        .attr("height", config.legendBoxSize)
        .attr("fill", function(d, g) { return config.colors(g); });

      //Create text next to squares
      vis.legend.selectAll(".legend-labels")
        .data(data).enter()
        .append("svg:text").classed("legend-labels", true)
        .attr("x", config.w - config.paddingX / 2 + (1.5 * config.legendBoxSize))
        .attr("y", function(d, i) { return i * 2 * config.legendBoxSize; })
        .attr("dy", 0.07 * config.legendBoxSize + "em")
        .attr("font-size", 11 * config.labelScale + "px")
        .attr("fill", "gray")
        .text(function(d) {
          return d.group;
        });
    }


    // show tooltip of vertices
    function verticesTooltipShow(d) {
      vis.verticesTooltip.style("opacity", 0.9)
        .html("<strong>Value</strong>: " + d.value + "<br />" +
          "<strong>Description</strong>: " + d.description + "<br />")
        .style("left", (d3.event.pageX) + "px")
        .style("top", (d3.event.pageY) + "px");
    }

    // hide tooltip of vertices
    function verticesTooltipHide() {
      vis.verticesTooltip.style("opacity", 0);
    }
  }
};
function radarDraw(scope, element) {
  /**
   * Angular variables
   *
   */
   
  // watch for changes on scope.data
  scope.$watch("[csv, config]", function() {
    var csv = scope.csv;
    var config = scope.config;
    var data = csv2json(csv);
    RadarChart.draw(element[0], data, config);  // call the D3 RadarChart.draw function to draw the vis on changes to data or config
  });


  // helper function csv2json to return json data from csv
  function csv2json(csv) {
    csv = csv.replace(/, /g, ","); // trim leading whitespace in csv file
    var json = d3.csv.parse(csv); // parse csv string into json
    // reshape json data
    var data = [];
    var groups = []; // track unique groups
    json.forEach(function(record) {
      var group = record.group;
      if (groups.indexOf(group) < 0) {
        groups.push(group); // push to unique groups tracking
        data.push({ // push group node in data
          group: group,
          axes: []
        });
      };
      data.forEach(function(d) {
        if (d.group === record.group) { // push record data into right group in data
          d.axes.push({
            axis: record.axis,
            value: parseInt(record.value),
            description: record.description
          });
        }
      });
    });
    return data;
  }
}
group, axis, value, description
Mercedes, mileage, 7, 
Mercedes, price, 10, 
Mercedes, safety, 8, 
Mercedes, performance, 9, 
Mercedes, interior, 7, 
Mercedes, warranty, 7, 
Honda, mileage, 8, 
Honda, price, 6, 
Honda, safety, 9, 
Honda, performance, 6, 
Honda, interior, 3, 
Honda, warranty, 9, 
Chevrolet, mileage, 5, 
Chevrolet, price, 4, 
Chevrolet, safety, 6, 
Chevrolet, performance, 4, 
Chevrolet, interior, 5, 
Chevrolet, warranty, 6, 
##Links
- [bl.ocks](http://bl.ocks.org/chrisrzhou/2421ac6541b68c1680f8) 
- [plunker](http://embed.plnkr.co/RC2NL1/preview)

------
##Description
- A radar chart visualizes multivariate data in a 2D chart of three or more quantitative variables represented on axes.
- This is a variation of the [original](http://bl.ocks.org/tpreusse/2bc99d74a461b8c0acb1) and [improved](http://bl.ocks.org/nbremer/6506614) D3 radar chart.
- Major improvements include:
  - Refactoring D3 components (`levels`, `labels`, `axes`, `polygons`, `legend`), which now can be controlled through the `config` object.
  - Abstracting the building and rendering portions of the D3 visualization.
  - Aside from the basic stacked view, this variation includes a facetting option to plot the graphs in a facet grid.
- Use the configuration parameters to adjust the plot to your tastes, and you can also choose to view the plots stacked vs facetted.
- The data input has to be a 4-column CSV (headers MUST be included) conforming to the data schema of:
  - `group (int/string)`: data to be grouped into an object to plot the required polygon on the radar chart.
  - `axis (int/string)`: the axis of the radar charts (dimensions of the multivariate data).
  - `value (int)`: the data value of the given record.
  - `description (int/string)`: not a mandatory field, and additional columns after this are accepted as well.
- All D3 logic is contained in the `radar.js` file.  You should be able to look at just this file if you intend to use the visualization logic without AngularJS.

------
##Files
- **`index.html`**: Main angular app connecting the D3 vis through an angular directive `<radar>`.
- **`app.js`**: Main angular app file connecting the DOM view with Javascript variables.  Contains directive `onReadFile` to handle file uploads and `radar` to re-render the D3 visualization on data updates.
- **`radar.js`**: All D3 logic is contained in this file.  You should use this file if you are looking solely to use D3 without AngularJS.
- **`radarDraw.js`**: This is the directive-link function called by the AngularJS directive `radar` in `app.js`.  Funnels the dataset from the angular app into the D3 drawing logic called from `radar.js`.
- **`style.css`**: stylesheet containing optional D3 classes that can be adjusted (commented out)
- **`data.csv`**: Three CSV-data files for sample downloads and uploads to the app.

------
##Notes
- You may notice Angular digest errors in the console due to `$scope.$watch`.  Not too familiar on resolving these issues.
- A big help from this [fiddle](http://jsfiddle.net/alexsuch/6aG4x/) to help implement an AngularJS `FileReader`.
group, axis, value, description
Bulbs, Jan, 0, 
Bulbs, Feb, 0, 
Bulbs, Mar, 0, 
Bulbs, Apr, 0, 
Bulbs, May, 0, 
Bulbs, Jun, 0, 
Bulbs, Jul, 0, 
Bulbs, Aug, 1500, 
Bulbs, Sep, 5000, 
Bulbs, Oct, 8500, 
Bulbs, Nov, 3500, 
Bulbs, Dec, 500, 
Seeds, Jan, 2500, 
Seeds, Feb, 5500, 
Seeds, Mar, 9000, 
Seeds, Apr, 6500, 
Seeds, May, 3500, 
Seeds, Jun, 0, 
Seeds, Jul, 0, 
Seeds, Aug, 0, 
Seeds, Sep, 0, 
Seeds, Oct, 0, 
Seeds, Nov, 0, 
Seeds, Dec, 0, 
Flowers, Jan, 500, 
Flowers, Feb, 750, 
Flowers, Mar, 1500, 
Flowers, Apr, 2000, 
Flowers, May, 5500, 
Flowers, Jun, 7500, 
Flowers, Jul, 8500, 
Flowers, Aug, 7000, 
Flowers, Sep, 3500, 
Flowers, Oct, 2500, 
Flowers, Nov, 500, 
Flowers, Dec, 100, 
Trees, Jan, 0, 
Trees, Feb, 1500, 
Trees, Mar, 2500, 
Trees, Apr, 4000, 
Trees, May, 3500, 
Trees, Jun, 1500, 
Trees, Jul, 800, 
Trees, Aug, 550, 
Trees, Sep, 2500, 
Trees, Oct, 6000, 
Trees, Nov, 5500, 
Trees, Dec, 3000, 
group, axis, value, description
Iron Man, Intelligence, 6, Smart entreprenuer
Iron Man, Strength, 6, Powered by his suit
Iron Man, Speed, 5, rocket boosters
Iron Man, Durability, 6, tough durable material
Iron Man, Energy, 6, 
Iron Man, Fighting Skills, 4, 
Hulk, Intelligence, 6, Scientist brilliance
Hulk, Strength, 7, Insanely strong
Hulk, Speed, 3, clumsy
Hulk, Durability, 7, Close to industructible
Hulk, Energy, 1, 
Hulk, Fighting Skills, 4, great at SMASHING
Thor, Intelligence, 2, not too bright
Thor, Strength, 7, god-like strength
Thor, Speed, 7, god-like speed
Thor, Durability, 6, god-like durability
Thor, Energy, 6, 
Thor, Fighting Skills, 4, quite low for a god???
Captain America, Intelligence, 3, only human
Captain America, Strength, 3,  only human
Captain America, Speed, 2,  only human
Captain America, Durability, 3,  only human
Captain America, Energy, 1,  only human
Captain America, Fighting Skills, 6, able to judge combat decisively