Location: PHPKode > projects > Obsessive Website Statistics > ows/plugins/35_ows_heatmap.php
<?php
/*
	$Id: 35_ows_heatmap.php 112 2007-11-21 14:27:19Z randomperson83 $

	Obsessive Web Statistics
    Copyright (C) 2007 Dustin Spicuzza <hide@address.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	TODO: Need to add more options to this plugin, different things to display
	
*/


class OWSHeatmap implements iPlugin, iFilterPlugin{

	var $prefix = '';

	function __construct(){
		$this->prefix = $this->getPluginId() . '_';
	}

	// this should return a unique ID identifying the plugin, should start with an alpha,
	// should use basename instead of just __FILE__ otherwise it could expose path information
	public function getPluginId(){
		return 'p'. md5(basename(__FILE__) . get_class());
	}


	// returns an associative array describing the plugin
	public function getPluginInformation(){
		 
		// automagically increment the revision number :)
		$revision = trim(str_replace('Rev:','',str_replace('$','','$Rev: 112 $')));
		return array(
			'author' => 'Dustin Spicuzza (OWS builtin)',
			'pluginName' => 'Heatmap Plugin',
			'version' => "1.0.$revision",
			'description' => 'Shows visual analysis using heatmaps',
			'url' => 'http://obsessive.sourceforge.net/'
		);
	}

	
	// returns name of filter to show to user
	public function getDisplayName(){
		return "Heatmap Analysis";
	}
	
	// this function outputs html which is displayed inside of a form
	// submit button is already defined for you
	public function showOptions(){

		echo '<div class="floater">
			<p><u>Heatmap Types</u></p>
			<table>
			<tr><td>Heat &quot;Source&quot;:</td><td>' . generate_select_from_array($this->prefix . 'heatkey','host',$this->heat_keys) . '</td></tr>
			<tr><td>Detail:</td><td>' . generate_select_from_array($this->prefix . 'details','hourly',$this->details) . '</td></tr>
			</table>
			</div>';
	
		show_common_limits();	
	}
	

	var $heat_keys = array(
		
		array('unihost','Unique Visitors'),
		array('unireferrer','Unique External Referrers'),
		array('referrer','External Referrers'),
		array('noreferrer','No Referrer'),
		array('pages','Pages'),
		array('hits','Hits'),
		array('bytes','Bytes Served')
	);
	
	var $details = array(
		array('daily','Daily'),
		array('hourly','Hourly')
	);

	// this function shows the filtered results
	public function showResults($domain){
	
		global $cfg;
	
		$heatkey = get_post_var($this->prefix . 'heatkey');
		$details = get_post_var($this->prefix . 'details');
	
		if ($details == 'daily')
			echo "<div class=\"floater\"><h4>Month-Day Heatmap: ";
		else
			echo "<div class=\"floater\"><h4>Hourly Heatmap: ";
	
		$query = new SQLSelect($domain);
	
		$d_dimension = $query->DIMENSION('date');
		$query->SELECT("$d_dimension.month");
		$query->SELECT("$d_dimension.day_of_month");
		
		if ($details == 'hourly'){
			$h_dimension = $query->DIMENSION('time');
			$query->SELECT("$h_dimension.hour");
		}
		
		
		switch($heatkey){
			case 'unihost':
				echo "Unique Visitors";
				
				$dimension = $query->DIMENSION('host');
				$query->SELECT("COUNT(DISTINCT $dimension.host)");
				break;
			
			case 'unireferrer':
				echo "Unique External Referrers";
				$dimension = $query->DIMENSION('referrer');
				$query->SELECT("COUNT(DISTINCT $dimension.referrer)");
				$query->WHERE("$dimension.is_external = FALSE");
				break;
			
			case 'referrer':
				echo "External Referrers";
				$dimension = $query->DIMENSION('referrer');
				$query->SELECT("COUNT($dimension.referrer)");
				$query->WHERE("$dimension.is_external = FALSE");
				break;
				
			case 'noreferrer':
				echo "Hits with no referrers";
				$dimension = $query->DIMENSION('referrer');
				$query->SELECT("COUNT($dimension.referrer)");
				$query->WHERE("$dimension.referrer = ''");
				break;
				
			case 'hits':
				echo "Hits";
				$query->SELECT("COUNT(" . $query->FACT_TABLE() . ".id)");
				break;

			case 'pages':
				echo "Pages";
				$dimension = $query->DIMENSION('request');
				$query->SELECT("COUNT($dimension.is_page)");
				$query->WHERE("$dimension.is_page = TRUE");
				break;
		
			case 'bytes':
				echo "Bandwidth";
				$dimension = $query->DIMENSION('bytes');
				$query->SELECT("SUM($dimension.bytes)");
				break;
		
		}
		
		echo "</h4>";
		
		$query->GROUP_BY("$d_dimension.month");
		$query->GROUP_BY("$d_dimension.day_of_month");
	
		$query->ORDER_BY("$d_dimension.month ASC");
		$query->ORDER_BY("$d_dimension.day_of_month ASC");
		
		if ($details == 'hourly'){
			$query->GROUP_BY("$h_dimension.hour");
			$query->ORDER_BY("$h_dimension.hour ASC");
		}
		
		if (!($q = $query->generateQuery()))
			return;

		if (!($result = db_query($q)))
			return;
		
		$rows = array();
		$cols = array();
		
		if ($details == 'daily'){
			// generate the item list
			$cur_month = 1;
			$cur_day = 1;
			$items = array();
			$i_count = 0;
			
			while($row = db_fetch_row($result)){
			
				while ($row[0] != $cur_month || $row[1] != $cur_day){
					
					$i_count += 1;
					
					$cur_day += 1;
					if ($cur_day == 32){
						$cur_day = 1;
						$cur_month += 1;
					}
				}
			
				$cur_day += 1;
				if ($cur_day == 32){
					$cur_day = 1;
					$cur_month += 1;
				}
				
				$items[$i_count++] = $row[2];
			}
			
			// generate the rows
			for ($i = 1;$i < 13;$i++)
				$rows[] = date('M',mktime(0,0,0,$i,1,2000));
			
			// generate the columns
			for ($i = 1; $i < 32;$i++)
				$cols[] = sprintf("%02d",$i);
		
		}else{
			// details == hourly
		
			// generate the item list
			$first = true;
			
			$items = array();
			$i_count = 0;
			
			while($row = db_fetch_row($result)){
			
				if ($first){
					$cur_month = $row[0];
					$cur_day = $row[1];
					$cur_hour = 0;
					$rows[] = "$cur_month/$cur_day";
					$first = false;
				}
			
				while ($row[0] != $cur_month || $row[1] != $cur_day || $row[2] != $cur_hour){
					
					$i_count += 1;
					$cur_hour += 1;
					
					if ($cur_hour == 24){
						$cur_hour = 0;
						$cur_day += 1;
						if ($cur_day == 32){
							$cur_day = 1;
							$cur_month += 1;
						}
						$rows[] = "$cur_month/$cur_day";
					}
				}
			
				$cur_hour += 1;
				if ($cur_hour == 24){
					$cur_hour = 0;
					$cur_day += 1;
					if ($cur_day == 32){
						$cur_day = 1;
						$cur_month += 1;
					}
					$rows[] = "$cur_month/$cur_day";
				}
				
				$items[$i_count++] = $row[3];
			}
			
			// generate the columns
			for ($i = 0; $i < 24;$i++)
				$cols[] = sprintf("%02d",$i);
		}
		
		if (count($items) < 1)
			echo "No results returned.";
		else
			// show the heatmap
			$this->heatmap($items, $rows, $cols, $heatkey == 'bytes');
	
	}
	
	/*
		displays a heatmap
		
		$items 			Array of numbers to be displayed, should be length(row X cols)
		$rows			row identifiers
		$cols			column identifiers
		$use_file_size	Use the file size for the key
			
	*/
	function heatmap($items, $rows, $cols, $use_file_size = false){
		
		// do the heatmap now, using the first three things
		echo "<table>";
		
		$max = max($items);
		$cur_item = 0;
		
		$cur_row = 0;
		$num_rows = count($rows);
		
		$cur_col = 1;
		$num_cols = count($cols);
		
		// row counter
		while ($cur_row != $num_rows){
			
			// header 
			if ($cur_col == 1)
				echo "<tr><td>" . $rows[$cur_row] . "</td>";
			
			
			if (isset($items[$cur_item]) && $items[$cur_item] > 0){
				echo "<td title=\"$items[$cur_item]\" style=\"background: " . $this->getHeatColor($items[$cur_item],$max) . ";\">&nbsp;</td>";
			}else
				echo "<td title=\"0\" style=\"background: #000000;\">&nbsp;</td>";
			
			// end condition for column
			if ($cur_col == $num_cols){
			
				$mx1 = intval(($max / $num_rows) * $cur_row);
			
				echo "<td style=\"background:#ffffff\">&nbsp;&nbsp;&nbsp;</td><td>" . ($use_file_size ? get_file_size($mx1) : $mx1) . "</td><td style=\"background: " .
					$this->getHeatColor($mx1,$max) . ";\">&nbsp;</td></tr>";
			
				$cur_col = 0;
				$cur_row += 1;
			}
			
			$cur_col += 1;
			$cur_item += 1;
			
		}
		
		echo "<tr><td>&nbsp;</td>";
		foreach ($cols as $col)
			echo "<td>" . $col . "</td>";
		
		echo "<td colspan=\"3\">&nbsp;</td></tr></table>";
		echo "</div>";
	}
	
	// returns a HTML color representing the heat color
	function getHeatColor($num, $max, $h_start = 320, $s_start = 0.8, $v_start = 100, $h_end = 360){
		
		$h = 0;		// 0 to 360
		$s = 0;		// 0 to 1
		$v = 0;		// 0 to 255

		$num = min($num,$max);
		
		$h = $h_start + (($h_end-$h_start) * ($num / ($max+1)));
		$s = $s_start + (1-$s_start) *($num / ($max+1));
		$v = $v_start + ((255-$v_start) * ($num / ($max+1)));
		
		//prn(hsv2rgb($h,$s,$v));
		list($r,$g,$b) = $this->hsv2rgb($h,$s,$v);
		
		return '#' . sprintf('%02X',$r) . sprintf('%02X',$g) . sprintf('%02X',$b);
	}
	
	
	// HSV transform, from wikipedia
	function hsv2rgb($h,$s,$v){
	
		$h = max(0,min(360,$h));
		$s = max(0,min(1,$s));
		$v = max(0,min(255,$v));
		
		$hi = intval(floor($h / 60) % 6);
		$f = $h / 60 - $hi;
		$p = $v * (1 - $s);
		$q = $v * (1 - $f * $s);
		$t = $v * (1 - (1 - $f) * $s);
		
		//echo "Hi: $hi, f: $f, p $p, q $q, t $t<br/>";
		
		switch ($hi){
			case 0:
				$r = $v; $g = $t; $b = $p;
				break;
			case 1:
				$r = $q; $g = $v; $b = $p;
				break;
			case 2:
				$r = $p; $g = $v; $b = $t;
				break;
			case 3: 
				$r = $p; $g = $q; $b = $v;
				break;
			case 4:
				$r = $t; $g = $p; $b = $v;
				break;
			case 5:
				$r = $v; $g = $p; $b = $q;
				break;
			default:
				$r = 0; $g = 0; $b = 0;
		}
	
		return array(intval($r),intval($g),intval($b));
	}
}

register_plugin('filter',new OWSHeatmap());

?>
Return current item: Obsessive Website Statistics