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