CWIS Developer Documentation
Graph.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: Graph.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2002-2017 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
10 class Graph
11 {
12  const TYPE_DATE = 1;
13  const TYPE_DATE_BAR = 2;
14 
15  const OK = 1;
16  const NO_DATA = 2;
17  const ERROR_UNKNOWN_TYPE = 3;
18 
19  const DAILY = 0;
20  const WEEKLY = 1;
21  const MONTHLY = 2;
22 
29  public function __construct($GraphType, array $GraphData)
30  {
31  if (count($GraphData)==0)
32  {
33  $this->Status = self::NO_DATA;
34  }
35  elseif ($GraphType == self::TYPE_DATE ||
36  $GraphType == self::TYPE_DATE_BAR )
37  {
38  $this->Status = self::OK;
39  # Convert input data into a form that Javascript will deal with
40 
41  if ($GraphType == self::TYPE_DATE)
42  {
43  $this->Data = $this->ToJsFormat($GraphData);
44  }
45  elseif ($GraphType == self::TYPE_DATE_BAR)
46  {
47  # Summarize the search data passed in into daily, weekly, monthly:
48  $DailyData = array();
49  $WeeklyData = array();
50  $MonthlyData = array();
51 
52  foreach ($GraphData as $Xval => $Yvals)
53  {
54  $DailyTS = strtotime( date('Y-m-d', $Xval) );
55  # Find the Monday preceeding this day for the weekly TS:
56  $WeeklyTS = $DailyTS - 86400* (date('N', $Xval) - 1);
57  $MonthlyTS = strtotime( date('Y-m-01', $Xval) );
58 
59  $this->AddToArray( $DailyData, $DailyTS, $Yvals );
60  $this->AddToArray( $WeeklyData, $WeeklyTS, $Yvals );
61  $this->AddToArray( $MonthlyData, $MonthlyTS, $Yvals);
62  }
63 
64  $this->Data = array(
65  "Daily" => $this->ToJsFormat( $DailyData ),
66  "Weekly" => $this->ToJsFormat( $WeeklyData ),
67  "Monthly"=> $this->ToJsFormat( $MonthlyData ) );
68  }
69 
70  $this->Type = $GraphType;
71 
72  $this->Width = 960;
73  $this->Height = 500;
74 
75  $this->LabelChars = strlen(max( array_map('max', $GraphData) ) ) ;
76 
77  $this->TopMargin = 5;
78  $this->LeftMargin = 5;
79  $this->RightMargin = 50;
80  $this->BottomMargin = 40;
81 
82  $this->LabelFontSize = 14;
83 
84  $this->Legend = array();
85 
86  $this->Scale = self::DAILY;
87  $this->Title = "";
88  }
89  else
90  {
91  $this->Status = self::ERROR_UNKNOWN_TYPE;
92  }
93  }
94 
99  public function Status()
100  {
101  return $this->Status;
102  }
103 
108  public function XLabel($Label)
109  {
110  $this->XLabel = $Label;
111  }
112 
117  public function YLabel($Label)
118  {
119  $this->YLabel = $Label;
120  }
121 
126  public function LabelFontSize($Size)
127  {
128  $this->LabelFontSize = $Size;
129  }
130 
135  public function TopMargin($Margin)
136  {
137  $this->TopMargin = $Margin;
138  }
139 
144  public function RightMargin($Margin)
145  {
146  $this->RightMargin = $Margin;
147  }
148 
153  public function BottomMargin($Margin)
154  {
155  $this->BottomMargin = $Margin;
156  }
157 
162  public function LeftMargin($Margin)
163  {
164  $this->LeftMargin = $Margin;
165  }
166 
171  public function Width($Width)
172  {
173  $this->Width = $Width;
174  }
175 
180  public function Height($Height)
181  {
182  $this->Height = $Height;
183  }
184 
189  public function Legend($Legend)
190  {
191  $this->Legend = $Legend;
192  }
193 
198  public function Scale($Scale)
199  {
200  $this->Scale = $Scale;
201  }
202 
208  public function Title($Title)
209  {
210  $this->Title = $Title;
211  }
212 
216  public function Display()
217  {
218  if ($this->Status == self::NO_DATA)
219  {
220  print $this->Title;
221  print "<p><em>No data to display</em></p>";
222  return;
223  }
224 
225  if ($this->Status != self::OK)
226  {
227  return;
228  }
229 
230  $ChartNumber = self::GetNumber();
231 
232  global $AF;
233  // @codingStandardsIgnoreStart
234  ?>
235 
236  <?PHP if($ChartNumber==0) { ?>
237  <script type="text/javascript" src="<?PHP $AF->PUIFile("d3.js"); ?>"></script>
238  <script type="text/javascript" src="<?PHP $AF->PUIFile("CW-Graph.js"); ?>"></script>
239  <style>
240  .cw-chart { width: 100%; }
241  .cw-chart-button { padding: 5px; margin-bottom: 15px; cursor: pointer; }
242  svg { font: 12px sans-serif;}
243  .axis { shape-rendering: crispEdges; }
244 
245  .axis path, .axis line {
246  fill: none;
247  stroke-width: .5px;
248  }
249 
250  .x.axis path { stroke: #000; }
251  .x.axis line { stroke: #fff; stroke-opacity: .5; }
252  .y.axis line { stroke: #ddd; }
253 
254  path.line {
255  fill: none;
256  stroke-width: 1.5px;
257  }
258 
259  rect.pane {
260  cursor: move;
261  fill: none;
262  pointer-events: all;
263  }
264 
265  .focus circle {
266  fill: none;
267  stroke: steelblue;
268  }
269 
270  .focus { fill: black; }
271 
272  .line.graph_color0 { stroke: #4D7588; }
273  .line.graph_color1 { stroke: ##975078; }
274 
275  .area.graph_color0 { fill: #4D7588; }
276  .area.graph_color1 { fill: #975078; }
277  .area.graph_color2 { fill: #818181; }
278  .area.graph_color3 { fill: #5CAA5C; }
279  .area.graph_color4 { fill: #DDBC6D; }
280 
281  </style>
282  <?PHP } ?>
283 
284  <style>
285  <?PHP if ($this->Type == self::TYPE_DATE) { ?>
286  .x-label<?PHP print($ChartNumber); ?> { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
287  .y-label<?PHP print($ChartNumber); ?> { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
288  <?PHP } elseif ($this->Type == self::TYPE_DATE_BAR) { ?>
289  .x-label<?PHP print($ChartNumber); ?>a { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
290  .y-label<?PHP print($ChartNumber); ?>a { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
291 
292  .x-label<?PHP print($ChartNumber); ?>b { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
293  .y-label<?PHP print($ChartNumber); ?>b { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
294 
295  .x-label<?PHP print($ChartNumber); ?>c { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
296  .y-label<?PHP print($ChartNumber); ?>c { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
297  <?PHP } ?>
298  </style>
299 
300  <?PHP if ($this->Type == self::TYPE_DATE) { ?>
301  <?PHP print $this->Title; ?>
302  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>"></div>
303  <?PHP } elseif ($this->Type == self::TYPE_DATE_BAR) { ?>
304  <div style="max-width: <?PHP print $this->Width; ?>px;">
305 
306  <div>
307  <span style="float: right; margin-top: 3px; padding-right: <?PHP print $this->LabelChars + 1 ; ?>em; margin-right: <?PHP print $this->RightMargin; ?>px; ">
308  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>a"
309  <?PHP if ($this->Scale == self::DAILY) { ?> style="font-weight: bold;" <?PHP } ?>
310  >Daily</span>|
311  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>b"
312  <?PHP if ($this->Scale == self::WEEKLY) { ?> style="font-weight: bold;" <?PHP } ?>
313  >Weekly</span>|
314  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>c"
315  <?PHP if ($this->Scale == self::MONTHLY) { ?> style="font-weight: bold;" <?PHP } ?>
316  >Monthly</span>
317  </span>
318  <?PHP print $this->Title; ?>
319  </div>
320 
321  <div id="chart<?PHP print $ChartNumber; ?>">
322  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>a"
323  <?PHP if ($this->Scale != self::DAILY) { ?> style="display: none;" <?PHP } ?> ></div>
324  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>b"
325  <?PHP if ($this->Scale != self::WEEKLY) { ?> style="display: none;" <?PHP } ?> ></div>
326  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>c"
327  <?PHP if ($this->Scale != self::MONTHLY) { ?> style="display: none;" <?PHP } ?> ></div>
328  </div>
329  </div>
330  <script type="text/javascript">
331  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').click( function(){
332  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'bold');
333  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'normal');
334  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'normal');
335  jQuery('#chart<?PHP print $ChartNumber; ?>a').show();
336  jQuery('#chart<?PHP print $ChartNumber; ?>b').hide();
337  jQuery('#chart<?PHP print $ChartNumber; ?>c').hide();
338  });
339  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').click( function(){
340  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'normal');
341  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'bold');
342  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'normal');
343  jQuery('#chart<?PHP print $ChartNumber; ?>a').hide();
344  jQuery('#chart<?PHP print $ChartNumber; ?>b').show();
345  jQuery('#chart<?PHP print $ChartNumber; ?>c').hide();
346  });
347  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').click( function(){
348  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'normal');
349  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'normal');
350  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'bold');
351  jQuery('#chart<?PHP print $ChartNumber; ?>a').hide();
352  jQuery('#chart<?PHP print $ChartNumber; ?>b').hide();
353  jQuery('#chart<?PHP print $ChartNumber; ?>c').show();
354  });
355  </script>
356  <?PHP } ?>
357 
358  <script type="text/javascript">
359  <?PHP if ($this->Type == self::TYPE_DATE ) { ?>
360  jQuery(document).ready(function(){
361  new LineDateGraph(<?PHP print($ChartNumber); ?>,
362  <?PHP print(json_encode($this->Data)); ?>,
363  "<?PHP print($this->XLabel); ?>",
364  "<?PHP print($this->YLabel); ?>",
365  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
366  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
367  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?> ); });
368  <?PHP } else if ($this->Type == self::TYPE_DATE_BAR) { ?>
369  jQuery(document).ready(function(){
370  new BarDateGraph(
371  "<?PHP print($ChartNumber); ?>a",
372  <?PHP print(json_encode($this->Data["Daily"])); ?>,
373  "<?PHP print($this->XLabel); ?>",
374  "<?PHP print($this->YLabel); ?> (Daily)",
375  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
376  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
377  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
378  <?PHP print(22*3600); ?>); });
379  jQuery(document).ready(function(){
380  new BarDateGraph(
381  "<?PHP print($ChartNumber); ?>b",
382  <?PHP print(json_encode($this->Data["Weekly"])); ?>,
383  "<?PHP print($this->XLabel); ?>",
384  "<?PHP print($this->YLabel); ?> (Weekly)",
385  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
386  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
387  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
388  <?PHP print(7*86400); ?>); });
389  jQuery(document).ready(function(){
390  new BarDateGraph(
391  "<?PHP print($ChartNumber); ?>c",
392  <?PHP print(json_encode($this->Data["Monthly"])); ?>,
393  "<?PHP print($this->XLabel); ?>",
394  "<?PHP print($this->YLabel); ?> (Monthly)",
395  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
396  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
397  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
398  <?PHP print(28*86400); ?>); });
399  <?PHP } ?>
400  </script>
401  <?PHP
402  // @codingStandardsIgnoreStart
403  }
404 
405  private $Status;
406  private static $ChartNumber = 0;
407 
408  protected $Data;
409  protected $Type;
410  protected $Height;
411  protected $Width;
412  protected $LabelChars;
413  protected $TopMargin;
414  protected $LeftMargin;
415  protected $BottomMargin;
416  protected $RightMargin;
417  protected $Legend;
418  protected $Scale;
419  protected $XLabel;
420  protected $YLabel;
421  protected $LabelFontSize;
422  protected $Title;
423 
426  private function GetNumber()
427  {
428  return self::$ChartNumber++;
429  }
430 
439  private function AddToArray(&$Array, $Key, $Value)
440  {
441  if (!isset($Array[$Key]))
442  {
443  $Array[$Key] = $Value;
444  }
445  else
446  {
447  $Array[$Key] = array_map( function($a,$b){ return $a+$b; },
448  $Array[$Key], $Value );
449  }
450  }
451 
460  private function ToJsFormat($Data, $IsDate=TRUE)
461  {
462  $Result = array();
463  foreach ($Data as $Xval => $Yvals)
464  {
465  $DataRow = array();
466 
467  if ($IsDate)
468  {
469  $DataRow["X"] = 1000 * $Xval;
470  }
471  else
472  {
473  $DataRow["X"] = $Xval;
474  }
475 
476  $Count = 0;
477  foreach ($Yvals as $Yval)
478  {
479  $DataRow["Y".$Count++] = $Yval;
480  }
481 
482  $Result[] = $DataRow;
483  }
484 
485  return $Result;
486  }
487 }