From 76d0f6238cb90db931ac2d116453cb3ca38b45bd Mon Sep 17 00:00:00 2001
From: alvaro <alvaro@alia.(none)>
Date: Sun, 14 Oct 2012 08:01:45 -0700
Subject: [PATCH] Adding D3 parallel coordinates

Improving D3force graph
---
 .../Haanga/Extension/Filter/D3ForceGraph.php  |  46 -----
 .../Haanga/Extension/Filter/D3forcegraph.php  | 125 +++++++------
 .../Filter/D3parallelcoordinates.php          | 169 ++++++++++++++++++
 3 files changed, 238 insertions(+), 102 deletions(-)
 delete mode 100644 lib/Haanga/lib/Haanga/Extension/Filter/D3ForceGraph.php
 create mode 100644 lib/Haanga/lib/Haanga/Extension/Filter/D3parallelcoordinates.php

diff --git a/lib/Haanga/lib/Haanga/Extension/Filter/D3ForceGraph.php b/lib/Haanga/lib/Haanga/Extension/Filter/D3ForceGraph.php
deleted file mode 100644
index a1637021..00000000
--- a/lib/Haanga/lib/Haanga/Extension/Filter/D3ForceGraph.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-class Haanga_Extension_Filter_D3ForceGraph{
-  public $is_safe = TRUE;
-  static function main($obj, $varname){
-
-  	$nodesArr = array();
-  	$n=0;
-  	$first="";
-  	$nodes = "";
-  	$links = "";
-  	$names = explode(",", $varname);
-  	if(count($names)==2){
-  	  foreach($obj as $k){
-  	  	if(!isset($nodesArr[$k->$names[0]->value])){
-  	  	  $nodesArr[$k->$names[0]->value] = $n++;
-  	  	  $nodes .= $first."{\"name\": \"".$k->$names[0]->value."\", \"group\": 1}";
-  	  	}
-  	  	if(!isset($nodesArr[$k->$names[1]->value])){
-  	  	  $nodesArr[$k->$names[1]->value] = $n++;
-  	  	  $nodes .= ",\n  {\"name\": \"".$k->$names[1]->value."\", \"group\": 1}";
-  	  	}
-  	  	$links .= $first."{\"source\": ".$nodesArr[$k->$names[0]->value].", \"target\": ".$nodesArr[$k->$names[1]->value].", \"value\": 10}";
-  	  	$first = ",\n  ";
-  	  } 
-  	}
-  	
-  	  	
-  	$json  ='{"nodes":['.$nodes.'],
-  	"links":['.$links.']}';
-  	
-  	
-  	$pre = '<script type="text/javascript" src="http://lodspeakr.org/extensions/haanga/filters/d3/js/d3.js"></script>
-  	<script type="text/javascript" src="http://lodspeakr.org/extensions/haanga/filters/d3/js/d3.layout.js"></script>
-  	<script type="text/javascript" src="http://lodspeakr.org/extensions/haanga/filters/d3/js/d3.geom.js"></script>
-  	<link href="http://lodspeakr.org/extensions/haanga/filters/d3/css/force.css" rel="stylesheet" type="text/css" />
-  	<script type="text/javascript">
-  	var url = "http://alvaro.graves.cl";
-  	var data = '.$json.';
-  	</script>';
-  	$post = '<div style="float: left;border-width: 1px; border-style: solid;" id="chart"></div>
-  	<script type="text/javascript" src="http://lodspeakr.org/extensions/haanga/filters/d3/js/force.js">
-  	</script>';
-  	return $pre.$post;
-  }
-}
diff --git a/lib/Haanga/lib/Haanga/Extension/Filter/D3forcegraph.php b/lib/Haanga/lib/Haanga/Extension/Filter/D3forcegraph.php
index 1b5885cf..7e51db38 100644
--- a/lib/Haanga/lib/Haanga/Extension/Filter/D3forcegraph.php
+++ b/lib/Haanga/lib/Haanga/Extension/Filter/D3forcegraph.php
@@ -11,6 +11,7 @@ class Haanga_Extension_Filter_D3ForceGraph{
   	$links = array();
   	$names = explode(",", $varname);
   	$varList = array();
+  	$randId = uniqid("_ID_");
 
   	foreach($names as $v){
   	  if(strpos($v,"=")){
@@ -33,7 +34,19 @@ class Haanga_Extension_Filter_D3ForceGraph{
   	  //$data .= "        data.addColumn('".$columnType."', '".$variable['name']."');\n";
   	}
 
-  	
+  	//options
+  	$options = array();
+  	$options['width'] = 960;
+  	$options['height'] = 500;
+  	$options['color'] = '#aec7e8';
+  	$options['radius'] = 10;
+  	for($z=2; $z < count($names); $z++){
+      $pair = explode("=", $names[$z]);
+      $key = trim($pair[0], "\" '");
+      $value = trim($pair[1], "\" '");
+      $options[$key] = $value;     
+    }
+
   	
   	foreach($obj as $k){
   	    $nameSource = $varList[0]['name'];
@@ -56,70 +69,70 @@ class Haanga_Extension_Filter_D3ForceGraph{
   	$json['links'] = $links;
   	
   	
-  	$pre = '<script src="http://d3js.org/d3.v2.min.js?2.9.3"></script>
+  	$pre = '<div id="'.$randId.'"></div><script src="http://d3js.org/d3.v2.min.js?2.9.3"></script>
 <script>
-
-var width = 960,
-    height = 500
-
-var svg = d3.select("body").append("svg")
-    .attr("width", width)
-    .attr("height", height);
-
-svg.append("svg:defs").append("svg:marker").attr("id", "marker")
-    .attr("viewBox", "0 -5 10 10")
-    .attr("refX", 15)
-    .attr("refY", -1.5)
-    .attr("markerWidth", 6)
-    .attr("markerHeight", 6)
-    .attr("orient", "auto")
+ function initD3ForceGraph'.$randId.'(json) {
+  
+  var width = '.$options['width'].',
+  height = '.$options['height'].'
+  
+  var svg = d3.select("#'.$randId.'").append("svg")
+  .attr("width", width)
+  .attr("height", height);
+  
+  svg.append("svg:defs").append("svg:marker").attr("id", "marker")
+  .attr("viewBox", "0 -5 10 10")
+  .attr("refX", 15)
+  .attr("refY", -1.5)
+  .attr("markerWidth", 6)
+  .attr("markerHeight", 6)
+  .attr("orient", "auto")
   .append("svg:path")
-    .attr("d", "M0,-5L10,0L0,5");
-
-    
-var force = d3.layout.force()
-    .gravity(.05)
-    .distance(100)
-    .charge(-100)
-    .size([width, height]);
-
- function initD3ForceGraph(json) {
+  .attr("d", "M0,-5L10,0L0,5");
+  
+  
+  var force = d3.layout.force()
+  .gravity(.05)
+  .distance(100)
+  .charge(-100)
+  .size([width, height]);
+  
   force
-      .nodes(json.nodes)
-      .links(json.links)
-      .start();
-
+  .nodes(json.nodes)
+  .links(json.links)
+  .start();
+  
   var link = svg.append("svg:g").selectAll("path")
-    .data(force.links())
-  .enter().append("svg:path")
-    .attr("class", function(d) { return "link " + d.type; })
-    .attr("marker-end", "url(#marker)");
-
+  .data(force.links())
+  .enter().append("svg:path").style("fill", "none").style("stroke", "#999").style("stroke-width", "1.8px")
+  .attr("class", function(d) { return "link " + d.type; })
+  .attr("marker-end", "url(#marker)");
+  
   var node = svg.selectAll(".node")
-      .data(json.nodes)
-    .enter().append("g")
-      .attr("class", "node")
-      .call(force.drag);
-      
-  node.append("circle").attr("r", 10).style("fill", "#aec7e8").style("stroke", "#798ba2")
- 
+  .data(json.nodes)
+  .enter().append("g")
+  .attr("class", "node")
+  .call(force.drag);
+  
+  node.append("circle").attr("r", '.$options['radius'].').style("fill", "'.$options['color'].'").style("stroke", "#798ba2")
+  
   node.append("text").style("font", "10px sans-serif")
-      .attr("dx", 12)
-      .attr("dy", ".35em")
-      .text(function(d) { return d.name });
-
+  .attr("dx", 12)
+  .attr("dy", ".35em")
+  .text(function(d) { return d.name });
+  
   force.on("tick", function() {
-    link.attr("d", function(d) {
-    var dx = d.target.x - d.source.x,
-        dy = d.target.y - d.source.y,
-        dr = Math.sqrt(dx * dx + dy * dy);
-    return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
-  });
-    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
+      link.attr("d", function(d) {
+          var dx = d.target.x - d.source.x,
+          dy = d.target.y - d.source.y,
+          dr = Math.sqrt(dx * dx + dy * dy);
+          return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
+      });
+      node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
   });
 }
-var jsonD3 = '.json_encode($json).';
-initD3ForceGraph(jsonD3)
+var jsonD3'.$randId.' = '.json_encode($json).';
+initD3ForceGraph'.$randId.'(jsonD3'.$randId.')
 </script>';
   	return $pre.$post;
   }
diff --git a/lib/Haanga/lib/Haanga/Extension/Filter/D3parallelcoordinates.php b/lib/Haanga/lib/Haanga/Extension/Filter/D3parallelcoordinates.php
new file mode 100644
index 00000000..56463e8c
--- /dev/null
+++ b/lib/Haanga/lib/Haanga/Extension/Filter/D3parallelcoordinates.php
@@ -0,0 +1,169 @@
+<?php
+
+class Haanga_Extension_Filter_D3ParallelCoordinates{
+  public $is_safe = TRUE;
+  static function main($obj, $varname){
+
+  	$nodesArr = array();
+  	$n=0;
+  	$first="";
+  	$nodes = array();
+  	$links = array();
+  	$names = explode(",", $varname);
+  	$varList = array();
+  	$randId = uniqid("_ID_");
+
+  	foreach($names as $v){
+  	  if(strpos($v,"=")){
+  	    break;
+  	  }
+  	  $variable['name'] = $v;
+  	  $variable['value'] = 'value';
+  	  if(strpos($v, ".")){
+  	    $aux = explode(".", $v);
+  	    $variable['name'] = $aux[0];
+  	    $variable['value'] = $aux[1];
+  	  }
+  	  $fieldCounter++;
+  	  $columnType = 'number';
+  	  if($firstColumn){
+  	  	$columnType = 'string';
+  	  	$firstColumn = false;
+  	  }
+  	  array_push($varList, $variable);
+  	  //$data .= "        data.addColumn('".$columnType."', '".$variable['name']."');\n";
+  	}
+  	//options
+  	$options = array();
+  	$options['width'] = 960;
+  	$options['height'] = 500;
+  	$options['color'] = '#aec7e8';
+  	$options['highlightedColor'] = $options['color'];
+  	$options['radius'] = 10;
+  	$options['highlightedStrokeWidth'] = '3px';
+  	$options['strokeWidth'] = '1px';
+  	for($z=2; $z < count($names); $z++){
+      $pair = explode("=", $names[$z]);
+      $key = trim($pair[0], "\" '");
+      $value = trim($pair[1], "\" '");
+      $options[$key] = $value;     
+    }
+
+  	$rows = array();
+  	foreach($obj as $k){
+  	  $row = array();
+  	  foreach($varList as $v){
+  	    $variable = $v['name'];
+  	    $val = $v['value'];
+  	    $row[$variable] = $k->$variable->$val;
+  	  }
+  	  array_push($rows, $row);
+  	}  	
+
+  	$json = $rows;
+  	
+  	
+  	$pre = '<div id="'.$randId.'"><div id="name'.$randId.'" style="font-family:sans-serif;font-size:15px;height:25px"><h2> </h2></div></div><script src="http://d3js.org/d3.v2.min.js?2.9.3"></script>
+<script>
+
+function initD3ParallelCoordinates'.$randId.'(json){
+var width = '.$options['width'].',
+    height = '.$options['height'].'
+
+var m = [30, 10, 10, 10],
+    w = '.$options['width'].' - m[1] - m[3],
+    h = '.$options['height'].' - m[0] - m[2];
+var line = d3.svg.line(),
+    axis = d3.svg.axis().orient("left"),
+    background,
+    foreground;
+
+var svg = d3.select("#'.$randId.'").append("svg")
+    .attr("width", width)
+    .attr("height", height)
+    .append("svg:g")
+    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");
+;
+    
+var x = d3.scale.ordinal().rangePoints([0, w], 1),
+    y = {};
+
+  // Extract the list of dimensions and create a scale for each.
+  x.domain(dimensions = d3.keys(json[0]).filter(function(d) {
+    return d != "p" && (y[d] = d3.scale.linear()
+        .domain(d3.extent(json, function(p) { return +p[d]; }))
+        .range([h, 0]));
+  }));
+
+  // Add grey background lines for context.
+  background = svg.append("g")
+      .attr("class", "background")
+    .selectAll("path")
+      .data(json)
+    .enter().append("path")
+      .attr("d", path);
+
+  // Add blue foreground lines for focus.
+  foreground = svg.append("g")
+      .attr("class", "foreground")
+    .selectAll("path")
+      .data(json)
+    .enter().append("path").style("stroke-width", "'.$options['strokeWidth'].'").style("stroke", "'.$options['color'].'")
+      .attr("d", path).attr("name", function(d){return d.p}).on("mouseover", mouseover).on("mouseout", mouseout);
+
+  // Add a group element for each dimension.
+  var g = svg.selectAll(".dimension")
+      .data(dimensions)
+    .enter().append("g")
+      .attr("class", "dimension")
+      .attr("transform", function(d) { return "translate(" + x(d) + ")"; });
+
+  // Add an axis and title.
+  g.append("g")
+      .attr("class", "axis")
+      .each(function(d) { d3.select(this).call(axis.scale(y[d])); })
+    .append("text")
+      .attr("text-anchor", "middle")
+      .attr("y", -9)
+      .text(String);
+
+  // Add and store a brush for each axis.
+  g.append("g")
+      .attr("class", "brush")
+      .each(function(d) { d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brush", brush)); })
+    .selectAll("rect")
+      .attr("x", -8)
+      .attr("width", 16);
+
+// Returns the path for a given data point.
+function path(d) {
+  return line(dimensions.map(function(p) { return [x(p), y[p](d[p])]; }));
+}
+
+// Handles a brush event, toggling the display of foreground lines.
+function brush() {
+  var actives = dimensions.filter(function(p) { return !y[p].brush.empty(); }),
+      extents = actives.map(function(p) { return y[p].brush.extent(); });
+  foreground.style("display", function(d) {
+    return actives.every(function(p, i) {
+      return extents[i][0] <= d[p] && d[p] <= extents[i][1];
+    }) ? null : "none";
+  });
+}
+
+function mouseover(){
+  d3.select(this).style("stroke-width", "'.$options['highlightedStrokeWidth'].'").style("stroke", "'.$options['highlightedColor'].'");
+  d3.select("#name'.$randId.'").html("<h2>"+d3.select(this).attr("name")+"</h2>");
+}
+function mouseout(){
+  d3.select(this).style("stroke-width", "'.$options['strokeWidth'].'").style("stroke", "'.$options['color'].'");
+  d3.select("#name'.$randId.'").html("<h2></h2>");
+}
+}
+    
+var jsonD3'.$randId.' = '.json_encode($json).';
+initD3ParallelCoordinates'.$randId.'(jsonD3'.$randId.')
+</script>';
+  	return $pre.$post;
+  }
+}
-- 
GitLab