//thanks http://dexvis.com/vis/blog/2013/mar/reusable6/html/ParallelCoordinates3.html and http://syntagmatic.github.com/parallel-coordinates/ ParallelCoordinates.prototype = new DexComponent(); ParallelCoordinates.constructor = ParallelCoordinates; function ParallelCoordinates(userConfig) { DexComponent.call(this, userConfig, { 'id' : "ParallelCoordinates", 'class' : "ParallelCoordinates", 'parent' : null, 'height' : 400, 'width' : 600, 'opacity' : 100, 'strokeWidth' : 4, 'axisFontSize' : 16, 'fontSize' : 12, 'color' : d3.scale.category20(), 'title' : '', 'columnNames' : [ "X", "Y" ], 'data' : [[0,0],[1,1],[2,4],[3,9],[4,16]], 'rows' : 0, 'columns' : [], 'xoffset' : 0, 'yoffset' : 0, 'normalize' : false, 'axisLabels' : { 'stagger' : false, 'yoffset' : -9, 'staggeredYOffset' : -18 } }); // Ugly, but my JavaScript is weak. When in handler functions // this seems to be the only way to get linked back to the // this.x variables. this.chart = this; } ParallelCoordinates.prototype.render = function() { this.update(); }; ParallelCoordinates.prototype.update = function () { // If we need to call super: //DexComponent.prototype.update.call(this); var chart = this.chart; var config = this.config; var numericColumns = dex.csv.getNumericColumnNames(config.columnNames, config.data); var jsonData = dex.csv.toJson(config.columnNames, config.data); var x = d3.scale.ordinal() .rangePoints([0, config.width], 1); var y = {}; var line = d3.svg.line(); var axis = d3.svg.axis().orient("left"); var background; var foreground; var chartContainer = config.parent.append("g") .attr("id", config["id"]) .attr("class", config["class"]) .attr("transform", "translate(" + config.xoffset + "," + config.yoffset + ")"); // Extract the list of dimensions and create a scale for each. //x.domain(dimensions = d3.keys(cars[0]).filter(function(d) //{ // return d != "name" && (y[d] = d3.scale.linear() // .domain(d3.extent(cars, function(p) { return +p[d]; })) // .range([height, 0])); //})); var allExtents = [] numericColumns.forEach(function (d) { allExtents = allExtents.concat(d3.extent(jsonData, function (p) { return +p[d]; })); }); var normalizedExtent = d3.extent(allExtents); //console.log("NE: " + normalizedExtent); // REM: Figure out how to switch over to consistent extends. Snapping. x.domain(dimensions = d3.keys(jsonData[0]).filter(function (d) { if (d === "name") return false; if (contains(numericColumns, d)) { var extent = d3.extent(jsonData, function (p) { return +p[d]; }); if (config.normalize) { extent = normalizedExtent; } y[d] = d3.scale.linear() .domain(extent) .range([config.height, 0]); allExtents.concat(extent); } else { y[d] = d3.scale.ordinal() .domain(jsonData.map(function (p) { return p[d]; })) .rangePoints([config.height, 0]); } return true; })); // Add grey background lines for context. background = chartContainer.append("g") .attr("class", "background") .selectAll("path") .data(jsonData) .enter().append("path") .attr("d", path) .attr("id", "fillpath"); foreground = chartContainer.append("g") .attr("fill", "none") .attr("stroke-opacity", config.opacity / 100.0) .selectAll("path") .data(jsonData) .enter().append("path") .attr("d", path) .attr("stroke", function (d, i) { return config.color(i); }) .attr("stroke-width", config.strokeWidth) .attr("title", function (d, i) { var info = "
| " + key + " | " + jsonData[i][key] + " |