/[drupal]/contributions/modules/carbon/carbon_chart.inc
ViewVC logotype

Contents of /contributions/modules/carbon/carbon_chart.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.13 - (show annotations) (download) (as text)
Tue May 12 13:45:17 2009 UTC (6 months, 2 weeks ago) by johnackers
Branch: MAIN
CVS Tags: DRUPAL-6--1-0, HEAD
Changes since 1.12: +9 -15 lines
File MIME type: text/x-php
make sure that bars displayed in the same order as specified in settings (e.g. heating, elec, car, surface, air).
1 <?php
2
3 /**
4 * $Id: carbon_chart.inc,v 1.12 2009/05/05 14:45:41 johnackers Exp $
5 *
6 * chart functions for drupal carbon module.
7 *
8 * Created on 29-jan-2006
9 */
10
11 /**
12 * append links for available charts for this data
13 */
14
15 function carbon_chart_link(&$links)
16 {
17 $query = $_GET['q'];
18 $links["bar_chart"] = array("title"=>"bar chart", "href"=>$query, "query"=>"chart=bar",
19 "attributes" => array("title"=>"add a bar chart to this page"));
20 $links["pie_chart"] = array("title"=>"pie chart", "href"=>$query, "query"=>"chart=pie",
21 "attributes" => array("title"=>"add a pie chart to this page"));
22 }
23
24 /**
25 * if the user has requested a chart in the query
26 * embed the IMG tags into the HTML
27 */
28
29 function carbon_chart_embed($default_chart, $firstdate, $dataset)
30 {
31 $chart = null ;
32 if (isset($_GET['chart']))
33 {
34 $chart = $_GET['chart'];
35 }
36 else // enhancement request - http://drupal.org/node/125959
37 { // allow the default chart type to be specified
38 $chart = $default_chart;
39 }
40
41 if (!empty($chart))
42 {
43 $providers = _get_carbon_chart_providers();
44 $provider = $providers[variable_get("carbon_chart_provider", 'internal')];
45 if (empty($provider) || $provider == 'internal')
46 {
47 $query = $_SERVER["QUERY_STRING"] ; // does this work with all servers, prefer to use drupal fn?
48 $link = "?".$query."&render=".$chart ;
49 if (isset($firstdate))
50 $link .= "&firstdate=".$firstdate ;
51 return "<a href='".$link."'><img src='".$link."' alt='".$chart." chart not loaded' style='margin:10px'></a>" ;
52 }
53 else
54 { // ignore - this is work in progress
55 $data_anon = array_values($dataset);
56 $chart = array(
57 '#plugin' => $provider, // Google Charts API will be used
58 '#type' => 'pie2D', // To show a simple 2D line chart
59 '#height' => 150, // in pixels
60 '#width' => 300, // in pixels
61 array_values($data_anon[0]['data'])
62 );
63 }
64 return charts_chart($chart);
65 }
66 }
67
68
69 /**
70 * Is this a request to render a particular chart
71 */
72
73 function carbon_chart_requested()
74 {
75 return isset($_GET["render"]);
76 }
77
78 /**
79 * get the start date which should be embedded in request
80 */
81
82 function carbon_chart_get_startdate()
83 {
84 return ($_GET["firstdate"]);
85 }
86
87
88 /**
89 * generate an image that contains the chart that had been
90 * embedded in the HTML
91 *
92 * @param an array containing multiple $key => $values where key
93 * is the name of the item and value is size of the item
94 */
95
96
97 function carbon_chart_generate($map, $title)
98 {
99 if (!function_exists("imagecreate"))
100 {
101 return("function imagecreate not available, check that gd is installed");
102 }
103 // $map = array("abc" => 344, "def" => 432);
104
105 $chart = $_GET['render'];
106 // make up the function name based on the render request
107 $fn = "carbon_".$chart."_chart";
108
109
110 if (function_exists($fn))
111 {
112 return $fn($map, $title);
113 }
114 else
115 return("This function does not exist: ". $fn);
116 }
117
118
119 /**
120 * create a bar chart as a png/image
121 * @param an array of $title => $amount
122 */
123
124
125 function carbon_bar_chart($dataset, $title)
126 {
127 // allow the chart to create a large vertical (scrollable) image
128 carbon_bar_chart_horiz($dataset, $title, 100, 50, 560, 10000);
129 }
130
131
132 /**
133 * bars go left to right
134 */
135
136 function carbon_bar_chart_horiz($dataset, $title, $min_width, $min_height, $max_width, $max_height)
137 {
138 if (empty($dataset))
139 return "empty array";
140
141 $chart = new HorizontalBarChart();
142 $chart->min_width = $min_width ;
143 $chart->max_width = $max_width ;
144 $chart->min_height = $min_height ;
145 $chart->max_height = $max_height ;
146
147 $chart->initialize($title, $dataset);
148 $image = imagecreate($chart->sizex, $chart->sizey);
149 define_palette($image);
150
151 $chart->posx = $chart->posy = 0 ;
152
153 $colorWhite = imagecolorallocate($image, 255, 255, 255);
154 imagefilledrectangle($image, 0, 0, $chart->sizex, $chart->sizey, $colorWhite);
155
156 header("Content-type: image/png");
157 $chart->render($image);
158 imagepng($image);
159 imagedestroy($image);
160 }
161
162
163 class HorizontalBarChart
164 {
165 var $min_width, $min_height, $max_width, $max_height ;
166 var $width ;
167
168 var $title, $titleheight = 20 ;
169
170 var $labelMaxWidth = 140 ; // large enough to accomodate the key text
171 var $labelHeight = 20 ; // the sector logos
172
173 var $barWidth = 30 ; // includes inter bar gap
174 var $barGap = 4 ;
175 var $barMaxLength = 0 ;
176 var $dataset ;
177
178 var $baseWidth = 3 ; // cosmetic only
179 var $baseLength ;
180
181 var $sizex, $sizey ; // calculated on initialize
182 var $posx, $posy ; // set by external functions to position object
183
184 var $horizontalGap = 20 ;
185 var $verticalGaps = 10 ;
186 // work out sizes
187
188
189 function initialize(&$title, &$dataset)
190 {
191 $this->title = $title ;
192 $this->dataset = $dataset;
193
194 $width = $this->barWidth * count($dataset);
195 $barMaxHeight = $this->max_height - $this->labelHeight ;
196 $barMinHeight = $this->min_height - $this->labelHeight ;
197
198 // adjust the bar width if the calculated width is very small
199
200 if ($width < $barMinHeight)
201 $this->barWidth = $barMinHeight / count($this->dataset);
202
203 // of the height is enormous, actually would be
204 // better to stop adding images.
205
206 if ($width > $barMaxHeight)
207 $this->barWidth = $barMaxHeight / count($this->dataset);
208
209 $this->baseLength = $this->barWidth * count($this->dataset);
210
211 $this->sizex = $this->max_width ; // use available space for now
212 $this->barMaxLength = $this->sizex - $this->baseWidth - $this->labelMaxWidth - $this->horizontalGap;
213
214 $this->sizey = $this->titleheight
215 + $this->verticalGaps + $this->baseLength
216 + $this->verticalGaps + $this->labelHeight;
217
218 // $this->title = "sizey ".$this->sizey." baselen ".$this->baseLength ;
219 }
220
221 function render(&$image)
222 {
223 global $customPalette, $black;
224
225 // write the title
226 imagestring($image, 3, $this->posx, $this->posy + $this->titleheight/8, $this->title, $black);
227 imagestring($image, 2, $this->posx, $this->posy + $this->titleheight/2, $this->comment, $black);
228
229
230 $this->posy += $this->titleheight + $this->verticalGaps; // move to top of bars
231 $colNext = 0 ;
232 $shadow = false ;
233 $colorInUse = array();
234
235 $bareBarWidth = $this->barWidth - $this->barGap ;
236
237 $max_value = 0 ;
238
239 $basex = $this->labelMaxWidth + $this->horizontalGap ;
240 $barx = $basex + $this->baseWidth;
241 ;
242 foreach($this->dataset as $key => $ds)
243 {
244 $value = $ds["data"];
245
246 if (is_array($value))
247 $subtotal = array_sum($value);
248 else
249 $subtotal = $value ;
250 $max_value = max($max_value, $subtotal);
251 }
252 $scaling = $max_value == 0 ? 1 : ($this->barMaxLength)/$max_value;
253
254 // put down a base line
255
256 $colorBase = imagecolorallocate($image, 128, 128, 128);
257 imagefilledrectangle(
258 $image, $basex, $this->posy - $this->barGap,
259 $basex + $this->baseWidth, $this->posy + $this->baseLength,
260 $colorBase);
261
262 // Create bars
263
264 $barNum = 0 ;
265 foreach ($this->dataset as $outerKey => $outerValue)
266 {
267 $value = $outerValue["data"];
268 $comment = $outerValue["comment"] ;
269
270 $sum = 0 ;
271 $innerDataset = null ;
272 if (is_array($value))
273 {
274 $innerDataset = $value;
275 $sum = array_sum($innerDataset);
276 }
277 else
278 {
279 // create an inner dataset of size 1
280 $innerDataset = array();
281 $innerDataset[$outerKey] = $sum = $value ;
282 }
283 //
284 // write a set of bars, one extending the other
285 //
286 $x = $barx ;
287 $y = $this->posy + $barNum*$this->barWidth;
288 create_palette($innerDataset, $image);
289
290 foreach ($customPalette as $key=>$col)
291 {
292 if (empty($innerDataset[$key]))
293 continue ;
294 $value = $innerDataset[$key];
295 $colorInUse[$key] = true ;
296 $barLength = $value * $scaling ;
297
298 // right_side, top, left_side, bottom
299
300 imagefilledrectangle($image, $x, $y, $x += $barLength, $y+$bareBarWidth, $col);
301 $x += 2 ; // leave a gap between the bars
302
303 // if ($shadow)
304 // imagefilledrectangle($image, $baseWidth, $y = $i*$barWidth+$bareBarWidth, $baseWidth+$barLength-3, $y += $shadow, $darkgray);
305
306 }
307 $firstLine = is_numeric($outerKey) ? "" : $outerKey . " - ";
308 imagestring($image, 2, 0, $y , $comment, $black);
309 imagestring($image, 2, 0, $y + 12, $firstLine.sprintf("%.0f", $sum), $black);
310 $barNum++ ;
311 }
312 // write the color keys below the bars
313
314 $labelx = $barx ;
315 $squareSize = 10 ;
316 $y += $this->barWidth + $this->verticalGaps ;
317 foreach ($customPalette as $name => $col)
318 {
319 if (!array_key_exists($name, $colorInUse))
320 continue ;
321 imagefilledrectangle($image, $labelx, $y, $labelx+$squareSize, $y+$squareSize, $col) ;
322 imagestring($image, 2, $labelx + 15, $y, $name, $black);
323 $labelx += + $squareSize + strlen($name) * 6 + 15 ;
324 }
325 }
326 }
327
328
329
330
331 function carbon_bar_chart_vert($dataset, $title, $min_width, $min_height, $max_width, $max_height)
332 {
333 $labelWidth = 200 ; // large enough to accomodate the key text
334 $barWidth = 40 ; // start with arbitrary width
335
336 // work out sizes
337 $width = $barWidth * count($dataset);
338 if ($width < $min_width)
339 $barWidth = $min_width / count($dataset);
340 if ($width > $max_width)
341 $barWidth = $max_width / count($dataset);
342
343 $imgWidth = $barWidth * count($dataset);
344 $imgHeight = $max_height ;
345
346 header("Content-type: image/png");
347
348 // Create image and define colors
349
350 $image=imagecreate($imgWidth, $imgHeight);
351 $colorWhite=imagecolorallocate($image, 255, 255, 255);
352 $colorGrey=imagecolorallocate($image, 192, 192, 192);
353 $colorBase=imagecolorallocate($image, 128, 128, 128);
354
355 $colorFG=imagecolorallocate($image, 64, 64, 64);
356 $colorBG=imagecolorallocate($image, 192, 192, 192);
357
358 $baseHeight = 2 ;
359 $bareBarWidth = $barWidth ;
360 $barBottom = $imgHeight - $baseHeight - 1;
361 $barTop = $labelWidth ;
362
363 $scaling = max($dataset) == 0 ? 1 : ($barBottom - $barTop)/max($dataset);
364
365 // put down a base line
366
367 imagefilledrectangle($image, 0, $imgHeight -$baseHeight, $imgWidth, $imgHeight, $colorBase);
368
369 // Create bar charts
370
371 $i = 0 ;
372 foreach ($dataset as $key => $value){
373
374 $barHeight = $value * $scaling ;
375 // right_side, top, left_side, bottom
376 imagefilledrectangle($image, $i*$bareBarWidth, $barBottom-$barHeight, ($i+1)*$bareBarWidth, $barBottom, $colorFG);
377 imagefilledrectangle($image, ($i*$bareBarWidth)+1, $barBottom-$barHeight+1, (($i+1)*$bareBarWidth)-5, $barBottom, $colorBG);
378 imagestringup($image, 2, $i*$bareBarWidth + $bareBarWidth/2 - 10, $barBottom-$barHeight-10, $key." - ".sprintf("%.0f", $value), 255);
379 $i++ ;
380 }
381 // Output graph and clear image from memory
382
383 imagepng($image);
384 imagedestroy($image);
385 }
386
387
388
389 /**
390 *
391 * create a pie chart as a png/image
392 * @param an array of $title => $amount
393 */
394
395
396 function carbon_pie_chart($outerset, $toptitle)
397 {
398 global $white, $black, $lightgray ;
399
400 $charts = array();
401 $imagex = 0 ;
402 $imagey = 0 ;
403 $x = 0 ;
404 $y = 0 ;
405 $maxwidth = 480 ;
406
407 $y = 40 ; // make room for title
408 $verticalGap = 10 ; // between charts
409 $i = 0 ;
410 foreach ($outerset as $title => $dataset)
411 {
412 $chart = new PieChart();
413 $legend = new Legend();
414
415 // calculate the sizeof the image and legend
416 $chart->initialize($title, $dataset);
417 $legend->initialize($dataset);
418
419 // poke values into class members for desired position
420 // of this pie in the image. First try to put new image
421 // on the same line
422
423 if ($x + max($chart->sizex, $legend->sizex) < $maxwidth)
424 {
425 $chart->posx = $legend->posx = $x ;
426 $chart->posy = $y ;
427 $legend->posy = $y + $chart->sizey; ;
428 }
429 else // else move to the next line
430 {
431 $chart->posx = $legend->posx = $x = 0 ;
432 $chart->posy = $y = $imagey ;
433 $legend->posy = $y + $chart->sizey ;
434 }
435 $x += 10 + max($chart->sizex, $legend->sizex);
436
437 $imagex = max($imagex, $x );
438 $imagey = max($imagey, $legend->posy + $legend->sizey + $verticalGap);
439
440 $charts[] = $chart ; // save for rendering
441 $charts[] = $legend ; // save for rendering
442 }
443
444 $image = imagecreate($imagex+1, $imagey+1);
445
446 define_palette($image);
447 imagefilledrectangle($image, 0, 0, $imagex, $imagey, $white) ;
448
449 imagestring($image, 5, 0, 15, $toptitle, $black);
450
451 $i = 0 ;
452 foreach ($charts as $chart)
453 {
454 // debug - draw an outline around the box we are going to fill
455 imagerectangle($image, $chart->posx, $chart->posy,
456 $chart->posx+$chart->sizex, $chart->posy+$chart->sizey, $lightgray) ;
457 $chart->render($image);
458 }
459
460
461 // flush image
462 header('Content-type: image/png');
463 imagepng($image);
464 imagedestroy($image);
465 }
466
467 /**
468 * create a basic color palette to use with pie charts
469 * (this may not be necessary, it seems everyone would need
470 * to do this)
471 */
472
473
474 function define_palette($image)
475 {
476 // allocate the CGA 16 color palette
477 global $standardPalette, $sparePalette, $black, $white, $lightgray ;
478 if (isset($standardPalette) && isset($sparePalette))
479 return ;
480
481 $black = imagecolorallocate($image, 0x00, 0x00, 0x00);
482 $blue = imagecolorallocate($image, 0x00, 0x00, 0xAA);
483 $green = imagecolorallocate($image, 0x00, 0xAA, 0x00);
484 $cyan = imagecolorallocate($image, 0x00, 0xAA, 0xAA);
485 $red = imagecolorallocate($image, 0xAA, 0x00, 0x00);
486 $magenta = imagecolorallocate($image, 0xAA, 0x00, 0xAA);
487 $brown = imagecolorallocate($image, 0xAA, 0x55, 0x00);
488 $lightgray= imagecolorallocate($image, 0xF8, 0xF8, 0xF8);
489 $darkgray = imagecolorallocate($image, 0x55, 0x55, 0x55);
490 $b_blue = imagecolorallocate($image, 0x55, 0x55, 0xFF);
491 $b_green = imagecolorallocate($image, 0x55, 0xFF, 0x55);
492 $b_cyan = imagecolorallocate($image, 0x55, 0xFF, 0xFF);
493 $b_red = imagecolorallocate($image, 0xFF, 0x55, 0x55);
494 $b_magenta= imagecolorallocate($image, 0xFF, 0x55, 0xFF);
495 $yellow = imagecolorallocate($image, 0xFF, 0xFF, 0x55);
496 $grey = imagecolorallocate($image, 0x80, 0x80, 0x80);
497 $white = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);
498
499
500 $sparePalette = array(
501 $brown, $cyan,
502 $b_red, $b_green, $b_blue,
503 $b_magenta, $yellow, $b_cyan, $black);
504
505 $standardPalette_not_used = array(
506 "heating" => $red,
507 "elec" => $blue,
508 "car" => $magenta,
509 "surface" => $green,
510 "air" => $b_blue,
511 "mixed" => $grey);
512
513 $p = variable_get("carbon_sectors", CARBON_PALETTE);
514 if (empty($p)) // reset the original string
515 variable_set("carbon_sectors", CARBON_PALETTE);
516
517 $sectors = explode(",", variable_get("carbon_sectors", CARBON_PALETTE));
518 $standardPalette = array();
519 foreach ($sectors as $sector)
520 {
521 $sector = trim($sector);
522 $pair = explode('=', $sector); $key = $pair[0] ; $val = $pair[1] ;
523
524 $standardPalette[$key] = imagecolorallocate($image, intval(substr($val, 0, 2),16),
525 intval(substr($val, 2, 2),16),
526 intval(substr($val, 4, 2),16));
527 }
528 }
529
530 /**
531 * create a custom palette to match the keys provided in the
532 * dataset. Where keys match known keys, use a preferred colour.
533 * Then use colours from the spare palette.
534 *
535 * Once all the colours are used, don't show anything else.
536 */
537
538
539 function create_palette($dataset, $image)
540 {
541 global $standardPalette, $sparePalette, $customPalette;
542
543 define_palette($image);
544
545 if (!isset($customPalette))
546 $customPalette = array_merge($standardPalette);
547
548 $keys_not_assigned = array_merge($dataset);
549
550 // has this key been assigned to a colour ?
551
552 foreach($customPalette as $key => $col)
553 {
554 if (isset($keys_not_assigned[$key]))
555 {
556 unset($keys_not_assigned[$key]); // remove from list
557 }
558 }
559
560 $i = 0 ; foreach($keys_not_assigned as $key => $object)
561 {
562 while ($i < count($sparePalette))
563 {
564 $col = $sparePalette[$i++];
565 if (in_array($col,$customPalette))
566 continue;
567 $customPalette[$key] = $col ;
568 unset($keys_not_assigned[$key]);
569 }
570 }
571 }
572
573 /**
574 * It is very painful using objects in PHP that has to run under version 4 PHP.
575 * To difference between 4 and 5 as described by others is that 4 passes objects
576 * by value and 5 copies objects by reference.
577 *
578 * However it is very useful when you rendering different shapes of objects to
579 * use objects. There are two methods used to display objects
580 *
581 * This class draws a pie chart.
582 *
583 * size() - allow the object to work out how much space it will take up on
584 * the canvas
585 *
586 * render() - draw the object on the canvas.
587 *
588 */
589
590
591 class PieChart
592 {
593 var $sizex, $sizey ; // calculated on initialize
594 var $posx, $posy ; // set by external functions to position object
595 var $title, $titleheight ;
596 var $comment ;
597 var $dataset ;
598 var $ak ;
599 var $radiusx = 60 ;
600 var $radiusy = 60 ;
601
602 function initialize(&$title, &$dataset)
603 {
604 $this->title = $title;
605 $this->dataset = $dataset["data"];
606 $this->comment = $dataset["comment"];
607 // create image
608 $this->ak = array_keys($this->dataset);
609 $this->titleheight = 30 ;
610 $this->sizey = $this->titleheight + $this->radiusy * 2 ;
611 $this->sizex = $this->radiusx * 2 ;
612 }
613
614 function render(&$image)
615 {
616 global $customPalette, $black;
617 $total = 0 ;
618
619 // write the title
620 imagestring($image, 3, $this->posx, $this->posy + 0, $this->title, $black);
621 imagestring($image, 2, $this->posx, $this->posy + $this->titleheight/2, $this->comment, $black);
622 $this->posy += $this->titleheight ;
623
624 // work out how many degrees each piece of the
625 // pie can be, making a total of 360 degrees.
626
627 foreach ($this->dataset as $key => $value)
628 {
629 $total += $value ;
630 }
631 $scaling = ($total == 0) ? 0 : 360/$total;
632
633 $i = 0 ;
634 $last = 0 ;
635
636 // imagestring($image, 3, 20, 20, $this->ak[0], $black);
637
638 create_palette($this->dataset, $image);
639
640 foreach ($customPalette as $key=>$col)
641 {
642 if (!isset($this->dataset[$key]))
643 continue ;
644 $value = $this->dataset[$key];
645 $next = $value * $scaling + $last;
646
647 // draw the pie
648 // cx, cy, w, h, s, e, col,
649 imagefilledarc($image,
650 $this->posx + $this->radiusx, $this->posy + $this->radiusy,
651 2 * $this->radiusx, 2*$this->radiusy,
652 $last, $next,
653 $col, IMG_ARC_PIE);
654
655 $last = $next ; $i++ ;
656 }
657 }
658 }
659
660 /**
661 * This class writes the legend for a pie chart or a bar chart
662 */
663
664
665 class Legend
666 {
667 var $sizex = 0, $sizey = 0 ;
668 var $posx, $posy ;
669
670 var $legend_height ;
671 var $dataset ;
672
673 function initialize(&$dataset)
674 {
675 global $customPalette;
676 $this->dataset = $dataset["data"] ;
677 $this->legend_height = 18;
678 $this->sizex = 150 ;
679
680 foreach ($this->dataset as $key=>$val)
681 {
682 if (empty($val))
683 continue ;
684 $this->sizey += $this->legend_height;
685 }
686 }
687
688 function render(&$image)
689 {
690 global $customPalette, $black;
691 $i = 0 ;
692 foreach ($customPalette as $key=>$col)
693 {
694 if (empty($this->dataset[$key]))
695 continue ;
696 $y = $this->posy + ($i * $this->legend_height) ;
697
698 // draw the key
699 imagefilledrectangle($image, $this->posx, $y, $this->posx+10, $y+10, $col);
700 //
701 // now write the entry into the keys.
702 //
703 imagestring($image, 2, $this->posx + 20, $y, $key." - ".sprintf("%.0f", $this->dataset[$key]), $black);
704 $i++ ;
705 }
706 }
707 }
708
709
710 ?>

  ViewVC Help
Powered by ViewVC 1.1.2