From 01c4148c49a12cbee60c5483d2b6268e67a59b53 Mon Sep 17 00:00:00 2001 From: alvaro <alvaro@graves.cl> Date: Fri, 31 May 2013 13:15:29 -0700 Subject: [PATCH] Added linechart and stacked column --- .../Haanga/Extension/Filter/D3linechart.php | 272 +++++++++++++++++ .../Extension/Filter/D3stackedcolumnchart.php | 273 ++++++++++++++++++ 2 files changed, 545 insertions(+) create mode 100644 lib/Haanga/lib/Haanga/Extension/Filter/D3linechart.php create mode 100644 lib/Haanga/lib/Haanga/Extension/Filter/D3stackedcolumnchart.php diff --git a/lib/Haanga/lib/Haanga/Extension/Filter/D3linechart.php b/lib/Haanga/lib/Haanga/Extension/Filter/D3linechart.php new file mode 100644 index 00000000..4ba053e4 --- /dev/null +++ b/lib/Haanga/lib/Haanga/Extension/Filter/D3linechart.php @@ -0,0 +1,272 @@ +<?php + +class Haanga_Extension_Filter_D3LineChart{ + public $is_safe = TRUE; + static function main($obj, $varname){ + $data = array(); + $i = 0; + $options = array(); + $randId = rand(); + $firstColumn = true; + $names = explode(",", $varname); + $j = 0; + + + $fieldCounter=0; + $varList = array(); + 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++; + array_push($varList, $variable); + } + + $columnsAsSeries = false; + $series = array(); + if($columnsAsSeries){ + foreach($obj as $k){ + $newItem = array(); + foreach($varList as $v){ + $name = $v['name']; + $val = $v['value']; + + if($j==0){ + //$newItem[$j]['x'] = $value; + }else{ + $series[$name]['key'] = $name; + $series[$name]['values'][] = $k->$name->$val; + } + $j++; + } + $i++; + $j=0; + // array_push($data, $newItem); + } + foreach($series as $v){ + $data[] = $v; + } + }else{ + foreach($obj as $k){ + $newSerie = array(); + $currentSerie = null; + $newItem = array(); + foreach($varList as $v){ + $name = $v['name']; + $val = $v['value']; + + if($j==0){ + $currentSerie = $k->$name->$val; + if(!isset($data[$currentSerie])){ $data[$currentSerie] = array();} + }elseif($j == 1){ + $newItem['key'] = $k->$name->$val; + }elseif($j == sizeof($varList)-1){ + $newItem['uri'] = $k->$name->$val; + $data[$currentSerie][] = $newItem; + $newItem = array(); + }else{ + $newItem['values'] = floatval($k->$name->$val); + } + $j++; + } + $i++; + $j=0; + // array_push($data, $newItem); + } + /* $data = array( + array('key' => 'zxc', 'values' => array( 1, 2)), + array('key' => 'asd', 'values' => array(10, 2)), + array('key' => 'zxc1', 'values' => array( 11, 2)), + array('key' => 'asd1', 'values' => array(21, 2)), + array('key' => 'zxc2', 'values' => array( 23, 2)), + array('key' => 'asd3', 'values' => array(20, 2)), + );*/ + } + + //Getting options + $options['height'] = 300; + $options['width'] = 1000; + $options['padding'] = 20; + $options['barsProportion'] = 0.8; + $options['legendSpace'] = 15; + $options['intermediateLines'] = 4; + $options['numberOfBars'] = 0; + $options['chartProportion'] = .8; + for($z=$fieldCounter; $z < count($names); $z++){ + $pair = explode("=", $names[$z]); + $key = trim($pair[0], "\" '"); + $value = trim($pair[1], "\" '"); + $options[$key] = $value; + } + + $divId = uniqid("d3linechart_div"); + $pre = "<div id='".$divId."'></div> + <script src='http://d3js.org/d3.v2.min.js?2.9.3'></script> + <script type='text/javascript'> + //Adding namespaces + d3.ns.prefix['vsr'] = 'http://purl.org/twc/vocab/vsr#'; + d3.ns.prefix['rdf'] = 'http://www.w3.org/2000/01/rdf-schema#'; + d3.ns.prefix['grddl'] = 'http://www.w3.org/2003/g/data-view#'; + + var options_$divId = ".json_encode($options)."; + var dataset_$divId = ".json_encode($data)."; + var color = d3.scale.category10(); + var stroke = function(d){ + s = ['', '5,5', '5,10', '10,5']; + return s[d%s.length]; + } + + var maxValue_$divId = getMax(dataset_".$divId."); + var svg = d3.selectAll('#".$divId."') + .append('svg') + .attr('width', options_$divId.width) + .attr('height', options_$divId.height) + .attr('xmlns:xmlns:vsr','http://purl.org/twc/vocab/vsr#') + .attr('xmlns:xmlns:grddl', 'http://www.w3.org/2003/g/data-view#') + .attr('xmlns:xmlns:rdf','http://www.w3.org/2000/01/rdf-schema#'); + + svg.append('svg:metadata').attr('grddl:grddl:transformation', 'https://raw.github.com/timrdf/vsr/master/src/xsl/grddl/svg.xsl'); + +//Tooltip +tooltip_$divId = svg.append('text').style('opacity', 0).style('font-family', 'sans-serif').style('font-size', '11px').style('stroke-width', '.5'); + +var maxHeight_$divId = options_$divId.chartProportion*options_$divId.height; + var labels_$divId = getLabels(dataset_$divId); + options_$divId.numberOfBars = getNumberOfBars(dataset_$divId); + + function getMax(d){ + maxValue = 0; + for(var i in d){ + e = d[i]; + for(var j in e){ + if(maxValue < parseInt(e[j].values)){ + maxValue = parseInt(e[j].values); + } + } + } + return maxValue+1; + } + + function getNumberOfBars(d){ + numberOfBars = 0; + for(var i in d){ + e = d[i]; + aux = 0; + for(var j in e){ + aux++; + } + if(aux > numberOfBars){ + numberOfBars = aux; + } + } + return numberOfBars; + } + + function getLabels(d){ + labels = []; + for(i in d){ + e = d[i]; + for(j in e){ + labels.push(e[j].key); + } + return labels + } + } + //Axis + var xaxis = svg.append('g'); + xaxis.append('line').style('stroke', 'black').style('stroke-width', '2px').attr('x1', 1+options_$divId.legendSpace).attr('y1', maxHeight_$divId).attr('x2', options_$divId.width+options_$divId.padding+ options_$divId.legendSpace).attr('y2', maxHeight_$divId) + xaxis.selectAll('line.stub') + var labels_$divId = xaxis.selectAll('text.xaxis') + .data(labels_".$divId.") + .enter().append('text').text(function(d){return d}) + .style('font-size', '12px').style('font-family', 'sans-serif') + .attr('class', 'xaxis') + .attr('x', function(d, i){return options_$divId.chartProportion*i*(parseInt(options_$divId.width)/ options_$divId.numberOfBars) + 4*options_$divId.padding + options_$divId.legendSpace}) + .attr('y', function(d, i){return maxHeight_$divId+30;}) + .attr('transform', function(d){return 'translate(-'+(this.getBBox().width/2)+')'}); + + + var yaxis = svg.append('g'); + yaxis.append('line').style('stroke', 'black').style('stroke-width', '2px').attr('x1', 1+options_$divId.padding + options_$divId.legendSpace).attr('y1', maxHeight_$divId).attr('x2', 1+options_$divId.padding + options_$divId.legendSpace).attr('y2', 1) + for(i=0; i<options_$divId.intermediateLines; i++){ + yaxis.append('line').style('stroke', 'grey').style('stroke-width', '1px').attr('x1', 1 + options_$divId.legendSpace).attr('y1', maxHeight_$divId*(i/options_$divId.intermediateLines)+1).attr('x2', options_$divId.width).attr('y2', maxHeight_$divId*(i/options_$divId.intermediateLines)) + } + + +//Values +var line = d3.svg.line() + .x(function(d, i){return options_$divId.chartProportion*i*(parseInt(options_$divId.width) / options_$divId.numberOfBars) + 4*options_$divId.padding + options_$divId.legendSpace}) + .y(function(d, i) {return (1-d.values/maxValue_$divId)*maxHeight_$divId; }); + +var j=0; +for(var k in dataset_".$divId."){ + svg.append('path').attr('class', 'line_$divId') + .datum(dataset_".$divId."[k]) + .attr('d', line) + .style('opacity', 0.8) + .style('stroke', function(d, i){return color(k)}) + .style('stroke-width', '2px') + .style('stroke-dasharray', stroke(j)) + .style('fill', 'none') .append('svg:metadata') + .append('vsr:vsr:depicts') + .attr('rdf:rdf:resource', function(d){target = ''; for(var x in d){target += d[x].uri+' ';} return target;}); + + svg.selectAll('circle.points_$divId_'+j).data(dataset_".$divId."[k]).enter() + .append('circle').attr('class', 'points_$divId_'+j) + .attr('r', 4) + .attr('cx', function(d, i){return options_$divId.chartProportion*i*(parseInt(options_$divId.width) / options_$divId.numberOfBars) + 4*options_$divId.padding + options_$divId.legendSpace}) + .attr('cy', function(d, i) {return (1-d.values/maxValue_$divId)*maxHeight_$divId; }) + .style('fill', function(d, i){return color(k)}).append('svg:metadata') + .append('vsr:vsr:depicts') + .attr('rdf:rdf:resource', function(d){ return d.uri}); + j++ +} + + d3.selectAll('rect.bar') + .on('mouseover', function(){ + d3.select(this).style('opacity', 1); + }).on('mouseout', function(){ + d3.select(this).style('opacity', 0.8); + }); +//Scale + for(i=0; i<options_$divId.intermediateLines; i++){ + yaxis.append('text') + .attr('x', 1) + .attr('y', maxHeight_$divId*(i/options_$divId.intermediateLines)+1) + .attr('font-family', 'sans-serif') + .attr('font-size', '10px') + .text(maxValue_$divId*(1-i/options_$divId.intermediateLines)) + .attr('transform', 'translate(0,10)'); + } + +//Events +svg.selectAll('circle').on('mouseover', function(e){ + tooltipColor = 'black'; + newX = parseFloat(d3.select(this).attr('cx')) - 10; + newY = parseFloat(d3.select(this).attr('cy')) - 5; + if(newY > maxHeight_$divId){ + newY -=10; + } + if(newY < 10){ + newY +=21; + } + + tooltip_$divId.style('opacity', 1).style('fill', tooltipColor).attr('y', newY).attr('x', newX).text(e.values); + d3.select(this).style('opacity', 1); + }).on('mouseout', function(){ + d3.select(this).style('opacity', 0.8); + tooltip_$divId.style('opacity', 0); + }); + </script> + "; + + return $pre; + } +} diff --git a/lib/Haanga/lib/Haanga/Extension/Filter/D3stackedcolumnchart.php b/lib/Haanga/lib/Haanga/Extension/Filter/D3stackedcolumnchart.php new file mode 100644 index 00000000..60b2c9be --- /dev/null +++ b/lib/Haanga/lib/Haanga/Extension/Filter/D3stackedcolumnchart.php @@ -0,0 +1,273 @@ +<?php + +class Haanga_Extension_Filter_D3StackedColumnChart{ + public $is_safe = TRUE; + static function main($obj, $varname){ + $data = array(); + $i = 0; + $options = array(); + $randId = rand(); + $firstColumn = true; + $names = explode(",", $varname); + $j = 0; + + + + $fieldCounter=0; + $varList = array(); + 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++; + array_push($varList, $variable); + } + + $columnsAsSeries = false; + $series = array(); + if($columnsAsSeries){ + foreach($obj as $k){ + $newItem = array(); + foreach($varList as $v){ + $name = $v['name']; + $val = $v['value']; + + if($j==0){ + //$newItem[$j]['x'] = $value; + }else{ + $series[$name]['key'] = $name; + $series[$name]['values'][] = $k->$name->$val; + } + $j++; + } + $i++; + $j=0; + // array_push($data, $newItem); + } + foreach($series as $v){ + $data[] = $v; + } + }else{ + foreach($obj as $k){ + $newSerie = array(); + $currentSerie = null; + $newItem = array(); + foreach($varList as $v){ + $name = $v['name']; + $val = $v['value']; + + if($j==0){ + $currentSerie = $k->$name->$val; + if(!isset($data[$currentSerie])){ $data[$currentSerie] = array();} + }elseif($j == 1){ + $newItem['key'] = $k->$name->$val; + }elseif($j == sizeof($varList)-1){ + $newItem['uri'] = $k->$name->$val; + $data[$currentSerie][] = $newItem; + $newItem = array(); + }else{ + $newItem['values'] = floatval($k->$name->$val); + } + $j++; + } + $i++; + $j=0; + // array_push($data, $newItem); + } + /* $data = array( + array('key' => 'zxc', 'values' => array( 1, 2)), + array('key' => 'asd', 'values' => array(10, 2)), + array('key' => 'zxc1', 'values' => array( 11, 2)), + array('key' => 'asd1', 'values' => array(21, 2)), + array('key' => 'zxc2', 'values' => array( 23, 2)), + array('key' => 'asd3', 'values' => array(20, 2)), + );*/ + } + + //Getting options + $options['height'] = 300; + $options['width'] = 1000; + $options['padding'] = 20; + $options['barsProportion'] = 0.8; + $options['legendSpace'] = 15; + $options['intermediateLines'] = 4; + $options['numberOfBars'] = 0; + for($z=$fieldCounter; $z < count($names); $z++){ + $pair = explode("=", $names[$z]); + $key = trim($pair[0], "\" '"); + $value = trim($pair[1], "\" '"); + $options[$key] = $value; + } + + $divId = uniqid("columnchart_div"); + $pre = "<div id='".$divId."' xmlns:vsr='http://purl.org/twc/vocab/vsr#' xmlns:rdf='http://www.w3.org/2000/01/rdf-schema#' xmlns:grddl='http://www.w3.org/2003/g/data-view#'> + </div> + <script src='http://d3js.org/d3.v2.min.js?2.9.3'></script> + <script type='text/javascript'> + //Adding namespaces + d3.ns.prefix['vsr'] = 'http://purl.org/twc/vocab/vsr#'; + d3.ns.prefix['rdf'] = 'http://www.w3.org/2000/01/rdf-schema#'; + d3.ns.prefix['grddl'] = 'http://www.w3.org/2003/g/data-view#'; + + var options_$divId = ".json_encode($options)."; + var dataset_$divId = ".json_encode($data)."; + var color = function(d){ + s = ['#5078a9', 'brown', 'gold', 'ForestGreen']; + return s[d%s.length]; + }; + + var maxValue_$divId = getMax(dataset_$divId); + options_$divId.numberOfBars = getNumberOfBars(dataset_$divId); + labels_$divId = getLabels(dataset_$divId); + + var svg = d3.selectAll('#".$divId."') + .append('svg') + .attr('width', options_$divId.width) + .attr('height', options_$divId.height) + .attr('xmlns:xmlns:vsr','http://purl.org/twc/vocab/vsr#') + .attr('xmlns:xmlns:grddl', 'http://www.w3.org/2003/g/data-view#') + .attr('xmlns:xmlns:rdf','http://www.w3.org/2000/01/rdf-schema#') + .attr('grddl:grddl:transformation', 'https://raw.github.com/timrdf/vsr/master/src/xsl/grddl/svg.xsl'); + + svg.append('svg:metadata').attr('grddl:grddl:transformation', 'https://raw.github.com/timrdf/vsr/master/src/xsl/grddl/svg.xsl'); + + var maxHeight_$divId = options_$divId.barsProportion*options_$divId.height; + + + function getMax(d){ + maxValues = []; + for(var i in d){ + e = d[i]; + for(var j in e){ + if(maxValues[e[j].key] == undefined){ + maxValues[e[j].key] = 0; + } + maxValues[e[j].key] += parseInt(e[j].values); + } + } + r = 0; + for(var i in maxValues){ + aux = parseInt(maxValues[i]); + if(aux > r){ + r = aux; + } + } + return r+1; + } + + function getNumberOfBars(d){ + numberOfBars = 0; + for(var i in d){ + e = d[i]; + aux = 0; + for(var j in e){ + aux++; + } + if(aux > numberOfBars){ + numberOfBars = aux; + } + } + return numberOfBars; + } + + function getLabels(d){ + labels = []; + for(i in d){ + e = d[i]; + for(j in e){ + labels.push(e[j].key); + } + return labels + } + } + +//Axis + var xaxis = svg.append('g'); + xaxis.append('line').style('stroke', 'black').style('stroke-width', '2px').attr('x1', 1+options_$divId.legendSpace).attr('y1', maxHeight_$divId).attr('x2', options_$divId.width+options_$divId.padding+ options_$divId.legendSpace).attr('y2', maxHeight_$divId) + xaxis.selectAll('line.stub') + var labels_$divId = xaxis.selectAll('text.xaxis') + .data(labels_$divId) + .enter().append('text').text(function(d){return d}) + .style('font-size', '12px').style('font-family', 'sans-serif') + .attr('class', 'xaxis') + .attr('x', function(d, i){return options_$divId.barsProportion * i* (parseInt(options_$divId.width) / options_$divId.numberOfBars) + 2*options_$divId.padding + options_$divId.legendSpace + this.getBBox().width}) + .attr('y', function(d, i){return maxHeight_$divId+30;}); +// .attr('transform', function(d, i){return ' rotate(-45 '+(options_$divId.barsProportion *i* (parseInt(options_$divId.width) / options_$divId.numberOfBars) + options_$divId.padding + options_$divId.legendSpace)+' '+(maxHeight_$divId+30)+') translate(-'+this.getBBox().width/2+',0)'}); + + + var yaxis = svg.append('g'); + yaxis.append('line').style('stroke', 'black').style('stroke-width', '2px').attr('x1', 1+options_$divId.padding + options_$divId.legendSpace).attr('y1', maxHeight_$divId).attr('x2', 1+options_$divId.padding + options_$divId.legendSpace).attr('y2', 1) + for(i=0; i<options_$divId.intermediateLines; i++){ + yaxis.append('line').style('stroke', 'grey').style('stroke-width', '1px').attr('x1', 1 + options_$divId.legendSpace).attr('y1', maxHeight_$divId*(i/options_$divId.intermediateLines)+1).attr('x2', options_$divId.width).attr('y2', maxHeight_$divId*(i/options_$divId.intermediateLines)) + } + + +//Values +baseline = []; +var j=0, l=0; +for(var k in dataset_$divId){ +j=0; + svg.selectAll('d.series') + .data(dataset_".$divId."[k]).enter() + .append('rect').attr('class', 'bar_$divId') + .attr('x', function(d, i) { + j++; + return options_$divId.barsProportion *j* (parseInt(options_$divId.width) / options_$divId.numberOfBars) + options_$divId.padding + options_$divId.legendSpace; + }) + .attr('y', function(d, i){ + if(baseline[d.key] == undefined){baseline[d.key]=maxHeight_$divId;} + r = maxHeight_$divId*(1-parseInt(d.values)/maxValue_$divId) - (maxHeight_$divId - baseline[d.key]); + baseline[d.key] = r + return r; + + }) + .attr('width', options_$divId.width / options_$divId.numberOfBars - options_$divId.padding) + .attr('height', function(d){ + return maxHeight_$divId*d.values/maxValue_$divId + }) + .style('opacity', 0.8).style('fill', function(d, i){return color(l)}) + .append('svg:metadata') + .append('vsr:vsr:depicts') + .attr('rdf:rdf:resource', function(d){return d.uri;}); + l++; +} + + +//Tooltip +tooltip_$divId = svg.append('text').style('opacity', 0).style('font-family', 'sans-serif').style('font-size', '11px').style('stroke-width', '.5'); + +//Events +svg.selectAll('rect.bar_$divId') + .on('mouseover', function(e){ + tooltipColor = 'black'; + newX = parseFloat(d3.select(this).attr('x')); + newY = parseFloat(d3.select(this).attr('y')); + if(newY > maxHeight_$divId){ + newY -=10; + } + if(newY < 10){ + newY +=11; + tooltipColor = 'white'; + } + tooltip_$divId.style('opacity', 1).style('fill', tooltipColor).attr('y', newY).attr('x', newX).text(e.values); + d3.select(this).style('opacity', 1); + }).on('mouseout', function(){ + d3.select(this).style('opacity', 0.8); + tooltip_$divId.style('opacity', 0); + }); + + for(i=0; i<options_$divId.intermediateLines; i++){ + yaxis.append('text').attr('x', 1).attr('y', maxHeight_$divId*(i/options_$divId.intermediateLines)+1).attr('font-family', 'sans-serif').attr('font-size', '10px').text(maxValue_$divId*(1-i/options_$divId.intermediateLines)).attr('transform', 'translate(0,10)'); + } + </script> + "; + return $pre; + } +} -- GitLab