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-2014 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
10 class Graph {
11  const TYPE_DATE = 1;
12  const TYPE_DATE_BAR = 2;
13 
14  const OK = 1;
15  const NO_DATA = 2;
16  const ERROR_UNKNOWN_TYPE = 3;
17 
18  const DAILY = 0;
19  const WEEKLY = 1;
20  const MONTHLY = 2;
21 
28  function __construct($GraphType, array $GraphData)
29  {
30  if (count($GraphData)==0)
31  {
32  $this->Status = self::NO_DATA;
33  }
34  elseif ($GraphType == self::TYPE_DATE ||
35  $GraphType == self::TYPE_DATE_BAR )
36  {
37  $this->Status = self::OK;
38  # Convert input data into a form that Javascript will deal with
39 
40  if ($GraphType == self::TYPE_DATE)
41  {
42  $this->Data = $this->ToJsFormat($GraphData);
43  }
44  elseif ($GraphType == self::TYPE_DATE_BAR)
45  {
46  # Summarize the search data passed in into daily, weekly, monthly:
47  $DailyData = array();
48  $WeeklyData = array();
49  $MonthlyData = array();
50 
51  foreach ($GraphData as $Xval => $Yvals)
52  {
53  $DailyTS = strtotime( date('Y-m-d', $Xval) );
54  # Find the Monday preceeding this day for the weekly TS:
55  $WeeklyTS = $DailyTS - 86400* (date('N',$Xval) - 1);
56  $MonthlyTS = strtotime( date('Y-m-01', $Xval) );
57 
58  $this->AddToArray( $DailyData, $DailyTS, $Yvals );
59  $this->AddToArray( $WeeklyData, $WeeklyTS, $Yvals );
60  $this->AddToArray( $MonthlyData, $MonthlyTS , $Yvals);
61  }
62 
63  $this->Data = array(
64  "Daily" => $this->ToJsFormat( $DailyData ),
65  "Weekly" => $this->ToJsFormat( $WeeklyData ),
66  "Monthly"=> $this->ToJsFormat( $MonthlyData ) );
67  }
68 
69  $this->Type = $GraphType;
70 
71  $this->Width = 960;
72  $this->Height = 500;
73 
74  $this->LabelChars = strlen(max( array_map('max', $GraphData) ) ) ;
75 
76  $this->TopMargin = 5;
77  $this->LeftMargin = 5;
78  $this->RightMargin = 50;
79  $this->BottomMargin = 40;
80 
81  $this->LabelFontSize = 14;
82 
83  $this->Legend = array();
84 
85  $this->Scale = self::DAILY;
86  $this->Title = "";
87  }
88  else
89  {
90  $this->Status = self::ERROR_UNKNOWN_TYPE;
91  }
92  }
93 
98  function Status(){ return $this->Status; }
99 
104  function XLabel($Label){ $this->XLabel = $Label; }
105 
110  function YLabel($Label){ $this->YLabel = $Label; }
111 
116  function LabelFontSize($X){ $this->LabelFontSize = $X; }
117 
122  function TopMargin($X) { $this->TopMargin = $X; }
123 
128  function RightMargin($X) { $this->RightMargin = $X; }
129 
134  function BottomMargin($X) { $this->BottomMargin = $X; }
135 
140  function LeftMargin($X) { $this->LeftMargin = $X; }
141 
146  function Width($X) { $this->Width = $X; }
147 
152  function Height($X) { $this->Height = $X; }
153 
158  function Legend($X) { $this->Legend = $X; }
159 
164  function Scale($X) { $this->Scale = $X; }
165 
170  function Title($X) { $this->Title = $X; }
171 
175  function Display()
176  {
177  if ($this->Status == self::NO_DATA)
178  {
179  print $this->Title;
180  print "<p><em>No data to display</em></p>";
181  return;
182  }
183 
184  if ($this->Status != self::OK)
185  return;
186 
187  $ChartNumber = self::GetNumber();
188 
189  global $AF;
190  ?>
191 
192  <?PHP if($ChartNumber==0) { ?>
193  <script type="text/javascript" src="<?PHP $AF->PUIFile("d3.js"); ?>"></script>
194  <script type="text/javascript" src="<?PHP $AF->PUIFile("CW-Graph.js"); ?>"></script>
195  <style>
196  .cw-chart { width: 100%; }
197  .cw-chart-button { padding: 5px; margin-bottom: 15px; cursor: pointer; }
198  svg { font: 12px sans-serif;}
199  .axis { shape-rendering: crispEdges; }
200 
201  .axis path, .axis line {
202  fill: none;
203  stroke-width: .5px;
204  }
205 
206  .x.axis path { stroke: #000; }
207  .x.axis line { stroke: #fff; stroke-opacity: .5; }
208  .y.axis line { stroke: #ddd; }
209 
210  path.line {
211  fill: none;
212  stroke-width: 1.5px;
213  }
214 
215  rect.pane {
216  cursor: move;
217  fill: none;
218  pointer-events: all;
219  }
220 
221  .focus circle {
222  fill: none;
223  stroke: steelblue;
224  }
225 
226  .focus { fill: black; }
227 
228  .line.graph_color0 { stroke: #4D7588; }
229  .line.graph_color1 { stroke: ##975078; }
230 
231  .area.graph_color0 { fill: #4D7588; }
232  .area.graph_color1 { fill: #975078; }
233  .area.graph_color2 { fill: #818181; }
234  .area.graph_color3 { fill: #5CAA5C; }
235  .area.graph_color4 { fill: #DDBC6D; }
236 
237  </style>
238  <?PHP } ?>
239 
240  <style>
241  <?PHP if ($this->Type == self::TYPE_DATE) { ?>
242  .x-label<?PHP print($ChartNumber); ?> { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
243  .y-label<?PHP print($ChartNumber); ?> { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
244  <?PHP } elseif ($this->Type == self::TYPE_DATE_BAR) { ?>
245  .x-label<?PHP print($ChartNumber); ?>a { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
246  .y-label<?PHP print($ChartNumber); ?>a { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
247 
248  .x-label<?PHP print($ChartNumber); ?>b { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
249  .y-label<?PHP print($ChartNumber); ?>b { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
250 
251  .x-label<?PHP print($ChartNumber); ?>c { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
252  .y-label<?PHP print($ChartNumber); ?>c { font-weight: bold; font-size: <?PHP print $this->LabelFontSize; ?>px; }
253  <?PHP } ?>
254  </style>
255 
256  <?PHP if ($this->Type == self::TYPE_DATE) { ?>
257  <?PHP print $this->Title; ?>
258  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>"></div>
259  <?PHP } elseif ($this->Type == self::TYPE_DATE_BAR) { ?>
260  <div style="max-width: <?PHP print $this->Width; ?>px;">
261 
262  <div>
263  <span style="float: right; margin-top: 3px; padding-right: <?PHP print $this->LabelChars + 1 ; ?>em; margin-right: <?PHP print $this->RightMargin; ?>px; ">
264  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>a"
265  <?PHP if ($this->Scale == self::DAILY) { ?> style="font-weight: bold;" <?PHP } ?>
266  >Daily</span>|
267  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>b"
268  <?PHP if ($this->Scale == self::WEEKLY) { ?> style="font-weight: bold;" <?PHP } ?>
269  >Weekly</span>|
270  <span class="cw-chart-button" id="cw-chart-button<?PHP print $ChartNumber; ?>c"
271  <?PHP if ($this->Scale == self::MONTHLY) { ?> style="font-weight: bold;" <?PHP } ?>
272  >Monthly</span>
273  </span>
274  <?PHP print $this->Title; ?>
275  </div>
276 
277  <div id="chart<?PHP print $ChartNumber; ?>">
278  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>a"
279  <?PHP if ($this->Scale != self::DAILY) { ?> style="display: none;" <?PHP } ?> ></div>
280  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>b"
281  <?PHP if ($this->Scale != self::WEEKLY) { ?> style="display: none;" <?PHP } ?> ></div>
282  <div class="cw-chart" id="chart<?PHP print $ChartNumber; ?>c"
283  <?PHP if ($this->Scale != self::MONTHLY) { ?> style="display: none;" <?PHP } ?> ></div>
284  </div>
285  </div>
286  <script type="text/javascript">
287  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').click( function(){
288  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'bold');
289  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'normal');
290  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'normal');
291  jQuery('#chart<?PHP print $ChartNumber; ?>a').show();
292  jQuery('#chart<?PHP print $ChartNumber; ?>b').hide();
293  jQuery('#chart<?PHP print $ChartNumber; ?>c').hide();
294  });
295  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').click( function(){
296  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'normal');
297  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'bold');
298  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'normal');
299  jQuery('#chart<?PHP print $ChartNumber; ?>a').hide();
300  jQuery('#chart<?PHP print $ChartNumber; ?>b').show();
301  jQuery('#chart<?PHP print $ChartNumber; ?>c').hide();
302  });
303  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').click( function(){
304  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>a').css('font-weight', 'normal');
305  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>b').css('font-weight', 'normal');
306  jQuery('#cw-chart-button<?PHP print $ChartNumber; ?>c').css('font-weight', 'bold');
307  jQuery('#chart<?PHP print $ChartNumber; ?>a').hide();
308  jQuery('#chart<?PHP print $ChartNumber; ?>b').hide();
309  jQuery('#chart<?PHP print $ChartNumber; ?>c').show();
310  });
311  </script>
312  <?PHP } ?>
313 
314  <script type="text/javascript">
315  <?PHP if ($this->Type == self::TYPE_DATE ) { ?>
316  jQuery(document).ready(function(){
317  new LineDateGraph(<?PHP print($ChartNumber); ?>,
318  <?PHP print(json_encode($this->Data)); ?>,
319  "<?PHP print($this->XLabel); ?>",
320  "<?PHP print($this->YLabel); ?>",
321  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
322  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
323  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?> ); });
324  <?PHP } else if ($this->Type == self::TYPE_DATE_BAR) { ?>
325  jQuery(document).ready(function(){
326  new BarDateGraph(
327  "<?PHP print($ChartNumber); ?>a",
328  <?PHP print(json_encode($this->Data["Daily"])); ?>,
329  "<?PHP print($this->XLabel); ?>",
330  "<?PHP print($this->YLabel); ?> (Daily)",
331  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
332  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
333  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
334  <?PHP print(22*3600); ?>); });
335  jQuery(document).ready(function(){
336  new BarDateGraph(
337  "<?PHP print($ChartNumber); ?>b",
338  <?PHP print(json_encode($this->Data["Weekly"])); ?>,
339  "<?PHP print($this->XLabel); ?>",
340  "<?PHP print($this->YLabel); ?> (Weekly)",
341  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
342  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
343  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
344  <?PHP print(7*86400); ?>); });
345  jQuery(document).ready(function(){
346  new BarDateGraph(
347  "<?PHP print($ChartNumber); ?>c",
348  <?PHP print(json_encode($this->Data["Monthly"])); ?>,
349  "<?PHP print($this->XLabel); ?>",
350  "<?PHP print($this->YLabel); ?> (Monthly)",
351  {top: <?PHP print $this->TopMargin; ?>, right: <?PHP print $this->RightMargin; ?>,
352  bottom: <?PHP print $this->BottomMargin; ?>, left: <?PHP print $this->LeftMargin; ?> },
353  <?PHP print($this->Width); ?>, <?PHP print($this->Height); ?>, <?PHP print(json_encode($this->Legend)); ?>,
354  <?PHP print(28*86400); ?>); });
355  <?PHP } ?>
356  </script>
357  <?PHP
358  }
359 
360  private $Status;
361 
362  private static $ChartNumber = 0;
363  private function GetNumber(){ return self::$ChartNumber++; }
364 
373  private function AddToArray(&$Array, $Key, $Value)
374  {
375  if (!isset($Array[$Key]))
376  $Array[$Key] = $Value;
377  else
378  $Array[$Key] = array_map( function($a,$b){ return $a+$b; },
379  $Array[$Key], $Value );
380 
381  }
382 
390  private function ToJsFormat($Data, $IsDate=TRUE)
391  {
392  $Result = array();
393  foreach ($Data as $Xval => $Yvals)
394  {
395  $DataRow = array();
396 
397 
398  if ($IsDate)
399  $DataRow["X"] = 1000 * $Xval;
400  else
401  $DataRow["X"] = $Xval;
402 
403  $Count = 0;
404  foreach ($Yvals as $Yval)
405  $DataRow[ "Y".$Count++ ] = $Yval;
406 
407  $Result []= $DataRow;
408  }
409 
410  return $Result;
411  }
412 }