7e7049237cc40799136ad79ea6d7ff7e6a33af06
[civicrm-core.git] / CRM / Utils / OpenFlashChart.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 require_once 'packages/OpenFlashChart/php-ofc-library/open-flash-chart.php';
37
38 /**
39 * Build various graphs using Open Flash Chart library.
40 */
41 class CRM_Utils_OpenFlashChart {
42
43 /**
44 * Colours.
45 * @var array
46 * @static
47 */
48 private static $_colours = array(
49 "#C3CC38",
50 "#C8B935",
51 "#CEA632",
52 "#D3932F",
53 "#D9802C",
54 "#FA6900",
55 "#DC9B57",
56 "#F78F01",
57 "#5AB56E",
58 "#6F8069",
59 "#C92200",
60 "#EB6C5C",
61 );
62
63 /**
64 * Build The Bar Gharph.
65 *
66 * @param array $params
67 * Assoc array of name/value pairs.
68 *
69 * @return object
70 * $chart object of open flash chart.
71 * @static
72 */
73 public static function &barChart(&$params) {
74 $chart = NULL;
75 if (empty($params)) {
76 return $chart;
77 }
78 if (empty($params['multiValues'])) {
79 $params['multiValues'] = array($params['values']);
80 }
81
82 $values = CRM_Utils_Array::value('multiValues', $params);
83 if (!is_array($values) || empty($values)) {
84 return $chart;
85 }
86
87 // get the required data.
88 $chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
89
90 $xValues = $yValues = array();
91 $xValues = array_keys($values[0]);
92 $yValues = array_values($values[0]);
93
94 //set y axis parameters.
95 $yMin = 0;
96
97 // calculate max scale for graph.
98 $yMax = ceil(max($yValues));
99 if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
100 $yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
101 }
102 $ySteps = $yMax / 5;
103
104 $bars = array();
105 $config = CRM_Core_Config::singleton();
106 $symbol = $config->defaultCurrencySymbol;
107 foreach ($values as $barCount => $barVal) {
108 $bars[$barCount] = new bar_glass();
109
110 $yValues = array_values($barVal);
111 foreach ($yValues as &$yVal) {
112 // type casting is required for chart to render values correctly
113 $yVal = (double) $yVal;
114 }
115 $bars[$barCount]->set_values($yValues);
116 if ($barCount > 0) {
117 // FIXME: for bars > 2, we'll need to come out with other colors
118 $bars[$barCount]->colour('#BF3B69');
119 }
120
121 if ($barKey = CRM_Utils_Array::value($barCount, CRM_Utils_Array::value('barKeys', $params))) {
122 $bars[$barCount]->key($barKey, 12);
123 }
124
125 // call user define function to handle on click event.
126 if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
127 $bars[$barCount]->set_on_click($onClickFunName);
128 }
129
130 // get the currency to set in tooltip.
131 $tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
132 $bars[$barCount]->set_tooltip($tooltip);
133 }
134
135 // create x axis label obj.
136 $xLabels = new x_axis_labels();
137 // set_labels function requires xValues array of string or x_axis_label
138 // so type casting array values to string values
139 array_walk($xValues, function (&$value, $index) {
140 $value = (string) $value;
141 });
142 $xLabels->set_labels($xValues);
143
144 // set angle for labels.
145 if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
146 $xLabels->rotate($xLabelAngle);
147 }
148
149 // create x axis obj.
150 $xAxis = new x_axis();
151 $xAxis->set_labels($xLabels);
152
153 //create y axis and set range.
154 $yAxis = new y_axis();
155 $yAxis->set_range($yMin, $yMax, $ySteps);
156
157 // create chart title obj.
158 $title = new title($chartTitle);
159
160 // create chart.
161 $chart = new open_flash_chart();
162
163 // add x axis w/ labels to chart.
164 $chart->set_x_axis($xAxis);
165
166 // add y axis values to chart.
167 $chart->add_y_axis($yAxis);
168
169 // set title to chart.
170 $chart->set_title($title);
171
172 // add bar element to chart.
173 foreach ($bars as $bar) {
174 $chart->add_element($bar);
175 }
176
177 // add x axis legend.
178 if ($xName = CRM_Utils_Array::value('xname', $params)) {
179 $xLegend = new x_legend($xName);
180 $xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
181 $chart->set_x_legend($xLegend);
182 }
183
184 // add y axis legend.
185 if ($yName = CRM_Utils_Array::value('yname', $params)) {
186 $yLegend = new y_legend($yName);
187 $yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
188 $chart->set_y_legend($yLegend);
189 }
190
191 return $chart;
192 }
193
194 /**
195 * Build The Pie Gharph.
196 *
197 * @param array $params
198 * Assoc array of name/value pairs.
199 *
200 * @return object
201 * $chart object of open flash chart.
202 * @static
203 */
204 public static function &pieChart(&$params) {
205 $chart = NULL;
206 if (empty($params)) {
207 return $chart;
208 }
209 $allValues = CRM_Utils_Array::value('values', $params);
210 if (!is_array($allValues) || empty($allValues)) {
211 return $chart;
212 }
213
214 // get the required data.
215 $values = array();
216 foreach ($allValues as $label => $value) {
217 $values[] = new pie_value((double) $value, $label);
218 }
219 $graphTitle = !empty($params['legend']) ? $params['legend'] : ts('Pie Chart');
220
221 //get the currency.
222 $config = CRM_Core_Config::singleton();
223 $symbol = $config->defaultCurrencySymbol;
224
225 $pie = new pie();
226 $pie->radius(100);
227
228 // call user define function to handle on click event.
229 if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
230 $pie->on_click($onClickFunName);
231 }
232
233 $pie->set_start_angle(35);
234 $pie->add_animation(new pie_fade());
235 $pie->add_animation(new pie_bounce(2));
236
237 // set the tooltip.
238 $tooltip = CRM_Utils_Array::value('tip', $params, "Amount is $symbol #val# of $symbol #total# <br>#percent#");
239 $pie->set_tooltip($tooltip);
240
241 // set colours.
242 $pie->set_colours(self::$_colours);
243
244 $pie->set_values($values);
245
246 //create chart.
247 $chart = new open_flash_chart();
248
249 // create chart title obj.
250 $title = new title($graphTitle);
251 $chart->set_title($title);
252
253 $chart->add_element($pie);
254 $chart->x_axis = NULL;
255
256 return $chart;
257 }
258
259 /**
260 * Build The 3-D Bar Gharph.
261 *
262 * @param array $params
263 * Assoc array of name/value pairs.
264 *
265 * @return object
266 * $chart object of open flash chart.
267 * @static
268 */
269 public static function &bar_3dChart(&$params) {
270 $chart = NULL;
271 if (empty($params)) {
272 return $chart;
273 }
274
275 // $params['values'] should contains the values for each
276 // criteria defined in $params['criteria']
277 $values = CRM_Utils_Array::value('values', $params);
278 $criterias = CRM_Utils_Array::value('criteria', $params);
279 if (!is_array($values) || empty($values) || !is_array($criterias) || empty($criterias)) {
280 return $chart;
281 }
282
283 // get the required data.
284 $xReferences = $xValueLabels = $xValues = $yValues = array();
285
286 foreach ($values as $xVal => $yVal) {
287 if (!is_array($yVal) || empty($yVal)) {
288 continue;
289 }
290
291 $xValueLabels[] = (string) $xVal;
292 foreach ($criterias as $criteria) {
293 $xReferences[$criteria][$xVal] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
294 $yValues[] = (double) CRM_Utils_Array::value($criteria, $yVal, 0);
295 }
296 }
297
298 if (empty($xReferences)) {
299
300 return $chart;
301
302 }
303
304 // get the currency.
305 $config = CRM_Core_Config::singleton();
306 $symbol = $config->defaultCurrencySymbol;
307
308 // set the tooltip.
309 $tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
310
311 $count = 0;
312 foreach ($xReferences as $criteria => $values) {
313 $toolTipVal = $tooltip;
314 // for seperate tooltip for each criteria
315 if (is_array($tooltip)) {
316 $toolTipVal = CRM_Utils_Array::value($criteria, $tooltip, "$symbol #val#");
317 }
318
319 // create bar_3d object
320 $xValues[$count] = new bar_3d();
321 // set colour pattel
322 $xValues[$count]->set_colour(self::$_colours[$count]);
323 // define colur pattel with bar criterias
324 $xValues[$count]->key((string) $criteria, 12);
325 // define bar chart values
326 $xValues[$count]->set_values(array_values($values));
327
328 // set tooltip
329 $xValues[$count]->set_tooltip($toolTipVal);
330 $count++;
331 }
332
333 $chartTitle = !empty($params['legend']) ? $params['legend'] : ts('Bar Chart');
334
335 //set y axis parameters.
336 $yMin = 0;
337
338 // calculate max scale for graph.
339 $yMax = ceil(max($yValues));
340 if ($mod = $yMax % (str_pad(5, strlen($yMax) - 1, 0))) {
341 $yMax += str_pad(5, strlen($yMax) - 1, 0) - $mod;
342 }
343
344 // if max value of y-axis <= 0, then set default values
345 if ($yMax <= 0) {
346 $ySteps = 1;
347 $yMax = 5;
348 }
349 else {
350 $ySteps = $yMax / 5;
351 }
352
353 // create x axis label obj.
354 $xLabels = new x_axis_labels();
355 $xLabels->set_labels($xValueLabels);
356
357 // set angle for labels.
358 if ($xLabelAngle = CRM_Utils_Array::value('xLabelAngle', $params)) {
359 $xLabels->rotate($xLabelAngle);
360 }
361
362 // create x axis obj.
363 $xAxis = new x_axis();
364 $xAxis->set_labels($xLabels);
365
366 //create y axis and set range.
367 $yAxis = new y_axis();
368 $yAxis->set_range($yMin, $yMax, $ySteps);
369
370 // create chart title obj.
371 $title = new title($chartTitle);
372
373 // create chart.
374 $chart = new open_flash_chart();
375
376 // add x axis w/ labels to chart.
377 $chart->set_x_axis($xAxis);
378
379 // add y axis values to chart.
380 $chart->add_y_axis($yAxis);
381
382 // set title to chart.
383 $chart->set_title($title);
384
385 foreach ($xValues as $bar) {
386 // add bar element to chart.
387 $chart->add_element($bar);
388 }
389
390 // add x axis legend.
391 if ($xName = CRM_Utils_Array::value('xname', $params)) {
392 $xLegend = new x_legend($xName);
393 $xLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
394 $chart->set_x_legend($xLegend);
395 }
396
397 // add y axis legend.
398 if ($yName = CRM_Utils_Array::value('yname', $params)) {
399 $yLegend = new y_legend($yName);
400 $yLegend->set_style("{font-size: 13px; color:#000000; font-family: Verdana; text-align: center;}");
401 $chart->set_y_legend($yLegend);
402 }
403
404 return $chart;
405 }
406
407 /**
408 * @param $rows
409 * @param $chart
410 * @param $interval
411 *
412 * @return array
413 */
414 public static function chart($rows, $chart, $interval) {
415 $chartData = $dateKeys = array();
416
417 switch ($interval) {
418 case 'Month':
419 foreach ($rows['receive_date'] as $key => $val) {
420 list($year, $month) = explode('-', $val);
421 $dateKeys[] = substr($rows['Month'][$key], 0, 3) . ' ' . $year;
422 }
423 $legend = ts('Monthly');
424 break;
425
426 case 'Quarter':
427 foreach ($rows['receive_date'] as $key => $val) {
428 list($year, $month) = explode('-', $val);
429 $dateKeys[] = 'Quarter ' . $rows['Quarter'][$key] . ' of ' . $year;
430 }
431 $legend = ts('Quarterly');
432 break;
433
434 case 'Week':
435 foreach ($rows['receive_date'] as $key => $val) {
436 list($year, $month) = explode('-', $val);
437 $dateKeys[] = 'Week ' . $rows['Week'][$key] . ' of ' . $year;
438 }
439 $legend = ts('Weekly');
440 break;
441
442 case 'Year':
443 foreach ($rows['receive_date'] as $key => $val) {
444 list($year, $month) = explode('-', $val);
445 $dateKeys[] = $year;
446 }
447 $legend = ts('Yearly');
448 break;
449 }
450
451 if (!empty($dateKeys)) {
452 $graph = array();
453 if (!array_key_exists('multiValue', $rows)) {
454 $rows['multiValue'] = array($rows['value']);
455 }
456 foreach ($rows['multiValue'] as $key => $val) {
457 $graph[$key] = array_combine($dateKeys, $rows['multiValue'][$key]);
458 }
459 $chartData =
460 array(
461 'legend' => "$legend " . CRM_Utils_Array::value('legend', $rows, ts('Contribution')) . ' ' . ts('Summary'),
462 'values' => $graph[0],
463 'multiValues' => $graph,
464 'barKeys' => CRM_Utils_Array::value('barKeys', $rows, array()),
465 );
466 }
467
468 // rotate the x labels.
469 $chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $rows, 0);
470 if (!empty($rows['tip'])) {
471 $chartData['tip'] = $rows['tip'];
472 }
473
474 //legend
475 $chartData['xname'] = CRM_Utils_Array::value('xname', $rows);
476 $chartData['yname'] = CRM_Utils_Array::value('yname', $rows);
477
478 // carry some chart params if pass.
479 foreach (array(
480 'xSize',
481 'ySize',
482 'divName'
483 ) as $f) {
484 if (!empty($rows[$f])) {
485 $chartData[$f] = $rows[$f];
486 }
487 }
488
489 return self::buildChart($chartData, $chart);
490 }
491
492 /**
493 * @param $rows
494 * @param $chart
495 * @param $interval
496 * @param $chartInfo
497 *
498 * @return array
499 */
500 public static function reportChart($rows, $chart, $interval, &$chartInfo) {
501 foreach ($interval as $key => $val) {
502 $graph[$val] = $rows['value'][$key];
503 }
504
505 $chartData = array(
506 'values' => $graph,
507 'legend' => $chartInfo['legend'],
508 'xname' => $chartInfo['xname'],
509 'yname' => $chartInfo['yname'],
510 );
511
512 // rotate the x labels.
513 $chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $chartInfo, 20);
514 if (!empty($chartInfo['tip'])) {
515 $chartData['tip'] = $chartInfo['tip'];
516 }
517
518 // carry some chart params if pass.
519 foreach (array(
520 'xSize',
521 'ySize',
522 'divName'
523 ) as $f) {
524 if (!empty($rows[$f])) {
525 $chartData[$f] = $rows[$f];
526 }
527 }
528
529 return self::buildChart($chartData, $chart);
530 }
531
532 /**
533 * @param array $params
534 * @param $chart
535 *
536 * @return array
537 */
538 public static function buildChart(&$params, $chart) {
539 $openFlashChart = array();
540 if ($chart && is_array($params) && !empty($params)) {
541 // build the chart objects.
542 $chartObj = CRM_Utils_OpenFlashChart::$chart($params);
543
544 $openFlashChart = array();
545 if ($chartObj) {
546 // calculate chart size.
547 $xSize = CRM_Utils_Array::value('xSize', $params, 400);
548 $ySize = CRM_Utils_Array::value('ySize', $params, 300);
549 if ($chart == 'barChart') {
550 $ySize = CRM_Utils_Array::value('ySize', $params, 250);
551 $xSize = 60 * count($params['values']);
552 //hack to show tooltip.
553 if ($xSize < 200) {
554 $xSize = (count($params['values']) > 1) ? 100 * count($params['values']) : 170;
555 }
556 elseif ($xSize > 600 && count($params['values']) > 1) {
557 $xSize = (count($params['values']) + 400 / count($params['values'])) * count($params['values']);
558 }
559 }
560
561 // generate unique id for this chart instance
562 $uniqueId = md5(uniqid(rand(), TRUE));
563
564 $openFlashChart["chart_{$uniqueId}"]['size'] = array('xSize' => $xSize, 'ySize' => $ySize);
565 $openFlashChart["chart_{$uniqueId}"]['object'] = $chartObj;
566
567 // assign chart data to template
568 $template = CRM_Core_Smarty::singleton();
569 $template->assign('uniqueId', $uniqueId);
570 $template->assign("openFlashChartData", json_encode($openFlashChart));
571 }
572 }
573
574 return $openFlashChart;
575 }
576 }