Umfragen
chart.class.php
Go to the documentation of this file.
1 <?php
2 /*
3  * chart.class.php
4  *
5  * Copyright 2013 Johannes <jojo@jojo-42>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA 02110-1301, USA.
21  *
22  *
23  */
24 
25 interface iChart{
26  public function get_config($name);
27  public function set_config($value,$value);
28  public function display();
29 
30 }
31 
32 
36 class chart_color implements iterator,arrayAccess{
37  private $position;
38  private $colors = array();
39  private $extraColors = null;
40 
41  private function make_extracolors(){
42  if( !isset($this->extraColors) ){
43  $tmp = $this->many_unique_random_colors(100,6,20);
44  foreach($tmp as &$c){
45  $c = $this->rgbbase255($c);
46  }
47  $this->extraColors = $tmp;
48  }
49  }
50 
51  /* ITERATOR*/
52  public function __construct(){
53  $this->position = 0;
54  }
55 
56  public function rewind(){
57  $this->position = 0;
58  }
59 
60  public function current(){
61  return $this->colors[$this->position];
62  }
63 
64  public function key(){
65  return $this->position;
66  }
67 
68  public function next(){
70  }
71 
72  public function valid(){
73  return isset( $this->colors[$this->position] );
74  }
75 
76 
77  /*ARRAYACCESS*/
78  public function offsetSet($offset,$value){
79  if( is_null($offset) ){
80  $this->colors[] = $value;
81  }else{
82  $this->colors[$offset] = $value;
83  }
84  }
85 
86  public function offsetGet($offset){
87  if( isset($this->colors[$offset]) ){
88  return $this->colors[$offset];
89  }else{
90  // make extraColors only if we need them and if the didn't need them before
91  $this->make_extracolors();
92  // return color as if extraColors would expand colors
93  if( (isset($this->extraColors)) AND (isset($this->extraColors[$offset-sizeof($this->colors)])) ){
94  return $this->extraColors[$offset->sizeof($this->colors)];
95  }
96  // sorry, we tried hard
97  //return null;
98  $c= $this->random_color();
99  return $this->rgbbase255($c);
100  }
101  }
102 
103  public function offsetExists($offset){
104  // first try if we have a color predefined
105  if( isset( $this->colors[$this->position] ) ){
106  return true;
107  }
108  // make extraColors only if we need them and if the didn't need them before
109  $this->make_extracolors();
110  // try if we find a color in extraColors
111  if( isset( $this->extraColors[sizeof($this->colors) - $this->position] ) ){
112  return true;
113  }
114  // sorry nothing found
115  return false;
116  }
117 
118  public function offsetUnset($offset){
119  //unset($this->colors[$offset]);
120  }
121 
122 
131  public function hsv2rgb($h,$s,$v){
132  if( !( ($h >= 0) AND ($h <= 360) ) ){
133  throw new exception("HSV error, H is not between 0 and 360");
134  }
135  if( !( ($s >= 0) AND ($s <= 1) ) ){
136  throw new exception("HSV error, S is not between 0 and 1");
137  }
138  if( !( ($v >= 0) AND ($v <= 1) ) ){
139  throw new exception("HSV error, V is not between 0 and 1");
140  }
141 
142  // see germans wikipedia page for HSV color
143  $hi = floor($h/60);
144  $f = ($h/60)-$hi;
145  $p = $v*(1-$s);
146  $q = $v*(1- ($s*$f) );
147  $t = $v*(1- $s*(1-$f) );
148 
149  if( ($hi == 0) OR ($hi == 6) ){
150  $rgb = array($v,$t,$p);
151  }else if( $hi == 1 ){
152  $rgb = array($q,$v,$p);
153  }else if( $hi == 2 ){
154  $rgb = array($p,$v,$t);
155  }else if( $hi == 3 ){
156  $rgb = array($p,$q,$v);
157  }else if( $hi == 4 ){
158  $rgb = array($t,$p,$v);
159  }else if( $hi == 5 ){
160  $rgb = array($v,$p,$q);
161  }else{
162  $rgb = array(1,1,1);
163  }
164  return $rgb;
165  }
166 
173  public function rgbbase255(array $rgb){
174  if( sizeof($rgb) == 3 ){
175  foreach( $rgb as &$c ){
176  if( ($c >= 0) AND ($c <= 1) ){
177  $c = $c*255;
178  $c = round($c);
179  }else{
180  throw new exception( "rgb value $c is not between 0 and 1" );
181  }
182  }
183  }else{
184  throw new exception( "rgb array has not exactly 3 values" );
185  }
186  return $rgb;
187  }
188 
196  public function rgb2html($r,$g,$b){
197  $color = array( $r,$g,$b );
198  foreach( $color as &$c ){
199  $c = dechex($c);
200  }
201  return "#{$color[0]}{$color[1]}{$color[2]}";
202  }
203 
212  public function rgba2html($r,$g,$b,$a){
213  return "rgba({$r},{$g},{$b},{$a})";
214  }
215 
216 
221  public function random_hue( ){
222  $phi_inv = 0.618033988749895;
223  $r = mt_rand();
224  $r = $r*$phi_inv;
225  $r = $r%360;
226  return $r;
227  }
228 
234  public function random_color($hue = null){
235  if(is_null($hue) ){
236  $phi_inv = 0.618033988749895;
237  $r = mt_rand();
238  $r = $r*$phi_inv;
239  $r = $r%360;
240  }else{
241  $r = $hue;
242  }
243 
244  $rgb = $this->hsv2rgb($r,0.5,0.95);
245  return $rgb;
246  }
247 
255  public function many_unique_random_colors($num,$thresold=10,$thresold2=30){
256  $maxTries = 10000;
257  /*$thresold = 10;
258  $thresold2 = 30;*/
259  $colors = array();
260  $hues = array();
261  $tries = 0;
262 
263  while( sizeof($hues) < $num ){
264  $h = $this->random_hue(); // get random hue
265 
266  // check if hue already exists
267  $okk = false;
268  $ok = array();
269  foreach( $hues as $hue ){
270  $b = $hue + $thresold;
271  $b2 = $hue - $thresold;
272  if( ($h>$b) OR ($h<$b2) ){
273  $ok[] = true;
274  }else{
275  $ok[] = false;
276  }
277  }
278  // if we find one false -> game over
279  $okk = (!in_array(false,$ok));
280 
281  // do not allow too similar colors nearby
282  $last = end($hues);
283  reset($hues);
284  if( $last != false ){
285  $b = $last + $thresold2;
286  $b2 = $last - $thresold2;
287  if( !(($h>$b) OR ($h<$b2)) ){
288  $okk = false;
289  }
290  }
291 
292  // but make the first color
293  if( sizeof($hues) == 0 ){
294  $okk = true;
295  }
296 
297  // set color or have another try
298  if( $okk === true ){
299  $hues[] = $h;
300  $colors[] = $this->random_color($h);
301  }else{
302  ++$tries;
303  }
304 
305  // do not enter ininite loop -> exit after max total tries
306  if($tries >= $maxTries ){
307  break;
308  }
309 
310  }
311  /* // sort rainbow colors
312  rsort($hues,SORT_NUMERIC);
313  foreach($hues as $hue){
314  $colors[] = $this->random_color($hue);
315  }*/
316  return $colors;
317  }
318 
322  public function test_many_colors(){
323  $thr1 = 6;
324  $thr2 = 20;
325  $num = 100;
326  $colors = $this->many_unique_random_colors($num,$thr1,$thr2);
327  $i = 1;
328  foreach($colors as $color){
329  $rgb = $this->rgbbase255($color);
330  $c = $this->rgb2html($rgb[0],$rgb[1],$rgb[2]);
331  echo "<div style='display:inline-block;width:3em;height:3em;background-color:$c'> </div>";
332  if( (($i%10) == 0) ){
333  echo "<br/>";
334  }
335  ++$i;
336  }
337 
338  $n = sizeof($colors);
339  echo "<p>Found $n colors from $num requested, using thresold1=$thr1 and thresold2=$thr2</p>";
340  }
341 
342 
343  public function getRGBA($offset,$a){
344  $color = $this->offsetGet($offset);
345  if( isset($color) ){
346  return $this->rgba2html($color[0],$color[1],$color[2],$a);
347  }else{
348  return "";
349  }
350  }
351 
352 }
353 
354 
355 class chart{
356  protected $data = array( );
357  protected $data_js = "";
358 
359  protected $colors;
360  protected $config;
361 
362  public function __construct(){
363  $this->colors = new chart_color();
364  $this->colors[] = array(220,220,220);
365  $this->colors[] = array(151,187,205);
366  $this->colors[] = array(151,160,205);
367  $this->colors[] = array(169,151,205);
368  $this->colors[] = array(196,151,205);
369  $this->colors[] = array(205,151,187);
370  $this->colors[] = array(205,151,160);
371  $this->colors[] = array(205,169,151);
372  $this->colors[] = array(205,196,151);
373  $this->colors[] = array(187,205,151);
374  $this->colors[] = array(160,205,151);
375  $this->colors[] = array(151,205,169);
376  $this->colors[] = array(151,205,196);
377  $this->colors[] = array(63,169,221);
378  $this->colors[] = array(63,90,221);
379  $this->colors[] = array(116,63,221);
380  $this->colors[] = array(195,63,221);
381  $this->colors[] = array(221,63,169);
382  $this->colors[] = array(221,63,90);
383  $this->colors[] = array(221,116,63);
384  $this->colors[] = array(221,195,63);
385  $this->colors[] = array(169,221,63);
386  $this->colors[] = array(90,221,63);
387  $this->colors[] = array(63,221,116);
388  $this->colors[] = array(63,221,195);
389  }
390 
391  public function test_many_colors(){
392  $n = 200;
393  $e = 0;
394  for( $i=0 ; $i<$n ; ++$i ){
395  $c = $this->colors->getRGBA($i,1);
396  if( $c != "" ){
397  echo "<div style='display:inline-block;width:3em;height:3em;background-color:$c'> </div>";
398  if( (($i%12) == 0) ){
399  echo "<br/>";
400  }
401  ++$e;
402  }
403  }
404  echo $e ;
405  }
406 
407  public function set_config($name,$value){
408  if( ($name == "width") OR ($name == "height") ){
409  if( is_numeric($value) ){
410  $this->config[$name] = $value;
411  }
412  }
413  }
414 
415  public function get_config($name){
416  if( ($name == "width") OR ($name == "height") ){
417  if( isset($this->config[$name]) ){
418  return $this->config[$name];
419  }else{
420  return 300;
421  }
422  }
423  }
424 
425  public function __get($name){
426  if( $name == "colors" ){
427  return $this->colors;
428  }
429  }
430 
431  public function __set($name,$value){
432  if( $name == "colors" ){
433  return;
434  }
435  }
436 
441  public function display_generic($method){
442  $r = rand(100000,1000000000);
443  $id = md5( serialize($this->data).$r );
444  $width = $this->get_config("width");
445  $height = $this->get_config("height");
446 
447  echo "<canvas id='$id' width='{$width}' height='{$height}'></canvas>";
448  echo "<script type='text/javascript'>";
449  echo $this->data_js;
450  //Get the context of the canvas element we want to select
451  echo "var ctx = document.getElementById('$id').getContext('2d');";
452  echo "var myNewChart = new Chart(ctx).$method(data);";
453  echo "</script>";
454  }
455 
456 }
457 
458 
459 class barChart extends chart implements iChart{
460 
461 
470  public function __construct($data){
472  $this->data = $data;
473 
474  // labels
475  $labels_str = "[";
476  foreach( $this->data["labels"] as $lab){
477  $labels_str .= '"'.$lab.'"'.',';
478  }
479  $labels_str = trim($labels_str,",");
480  $labels_str = $labels_str."]";
481 
482  // data
483  $datasets_str = "";
484  $c = 0;
485  foreach( $this->data["datasets"] as $set ){
486  $set_str = '{';
487  // color , fillcolor
488  $color = $this->colors->getRGBA($c,1);
489  $color2 = $this->colors->getRGBA($c,0.75);
490  $set_str .= 'fillColor : "'.$color2.'",';
491  $set_str .= 'strokeColor : "'.$color.'",';
492 
493  $set_str .= 'data : ';
494  $set_str .= '[';
495  foreach( $set as $d ){
496  $set_str .= $d.',';
497  }
498  $set_str = trim($set_str,",");
499  $set_str .= ']';
500  $set_str .= '},';
501  $datasets_str .= $set_str;
502  ++$c;
503  }
504  $datasets_str = trim($datasets_str,",");
505 
506  // construct js data array
507  $this->data_js = '
508  var data = {
509  labels : '.$labels_str.',
510  datasets : [ '.$datasets_str.' ]
511  };';
512 
513  }
514 
515  public function display(){
516  $r = rand(100000,1000000000);
517  $id = md5( serialize($this->data).$r );
518  $width = $this->get_config("width");
519  $height = $this->get_config("height");
520  /*$max = 0;
521  foreach( $this->data["datasets"] as $set){
522  foreach( $set as $val ){
523  if( $val > $max ){
524  $max = $val;
525  }
526  }
527  }
528  ++$max; // extra line
529  */
530  echo "<canvas id='$id' width='{$width}' height='{$height}'></canvas>";
531  echo "<script type='text/javascript'>";
532  echo $this->data_js;
533  /* // using custom scales (N step)
534  echo "var conf = {
535  scaleOverride : true,
536  //** Required if scaleOverride is true **
537  //Number - The number of steps in a hard coded scale
538  scaleSteps : $max,
539  //Number - The value jump in the hard coded scale
540  scaleStepWidth : 1,
541  //Number - The scale starting value
542  scaleStartValue : 0,
543  barValueSpacing : 10
544  };";
545  */
546  //Get the context of the canvas element we want to select
547  echo "var conf = {};";
548  echo "var ctx = document.getElementById('$id').getContext('2d');";
549  echo "var myNewChart = new Chart(ctx).Bar(data,conf);";
550  echo "</script>";
551  }
552 }
553 
554 
555 
556 
557 
558 
559 class radarChart extends chart{
560 
561 
570  public function __construct($data){
572  $this->data = $data;
573 
574  // labels
575  $labels_str = "[";
576  foreach( $this->data["labels"] as $lab){
577  $labels_str .= '"'.$lab.'"'.',';
578  }
579  $labels_str = trim($labels_str,",");
580  $labels_str = $labels_str."]";
581 
582  // data
583  $datasets_str = "";
584  $c = 0;
585  foreach( $this->data["datasets"] as $set ){
586  $set_str = '{';
587  // color , fillcolor
588  $color = $this->colors->getRGBA($c,1);
589  $color2 = $this->colors->getRGBA($c,0.75);
590  $set_str .= 'fillColor : "'.$color.'",';
591  $set_str .= 'strokeColor : "'.$color2.'",';
592  $set_str .= 'pointColor : "'.$color.'",';
593  $set_str .= 'pointStrokeColor : "#FFF",';
594 
595  $set_str .= 'data : ';
596  $set_str .= '[';
597  foreach( $set as $d ){
598  $set_str .= $d.',';
599  }
600  $set_str = trim($set_str,",");
601  $set_str .= ']';
602  $set_str .= '},';
603  $datasets_str .= $set_str;
604  ++$c;
605  }
606  $datasets_str = trim($datasets_str,",");
607 
608  // construct js data array
609  $this->data_js = '
610  var data = {
611  labels : '.$labels_str.',
612  datasets : [ '.$datasets_str.' ]
613  };';
614 
615  }
616 
617  public function display(){
618  parent::display_generic("Radar");
619  }
620 }
621 
622 
623 
624 
625 class pieChart extends chart{
626 
634  public function __construct($data){
636 
637  $this->data = $data;
638 
639  $c =0;
640  $this->data_js ='var data = [';
641  foreach( $this->data as $d){
642  $color = $this->colors->getRGBA($c,1);
643 
644  $this->data_js .= '{';
645  $this->data_js .= 'value: ';
646  $this->data_js .= $d;
647  $this->data_js .= ',color: ';
648  $this->data_js .= '"'.$color.'"';
649  $this->data_js .= '},';
650  ++$c;
651  }
652  $this->data_js = trim($this->data_js,",");
653  $this->data_js .= '];';
654  }
655 
656  public function display(){
658  }
659 
660 }
661 
662 class doughnutChart extends chart{
663 
664 
672  public function __construct($data){
674  $this->data = $data;
675 
676  $c =0;
677  $this->data_js ='var data = [';
678  foreach( $this->data as $d){
679  $color = $this->colors->getRGBA($c,1);
680 
681  $this->data_js .= '{';
682  $this->data_js .= 'value: ';
683  $this->data_js .= $d;
684  $this->data_js .= ',color: ';
685  $this->data_js .= '"'.$color.'"';
686  $this->data_js .= '},';
687  ++$c;
688  }
689  $this->data_js = trim($this->data_js,",");
690  $this->data_js .= '];';
691  }
692 
693  public function display(){
694  parent::display_generic("Doughnut");
695  }
696 
697 
698 }
699 
700 // ! no ending newLine
701 ?>