PHP Classes

File: heatclass.php

Recommend this page to a friend!
  Classes of Greg Bulmash   Brainhandles Heatmap   heatclass.php   Download  
File: heatclass.php
Role: Class source
Content type: text/plain
Description: Main Class File
Class: Brainhandles Heatmap
Generate heatmap images from a set of points
Author: By
Last change:
Date: 12 years ago
Size: 11,838 bytes
 

Contents

Class file image Download
<?php /*============================================================ The Brainhandles.com PHP HeatMap Class by Greg Bulmash http://www.brainhandles.com/techno-thoughts/the-brainhandles-heatmap-php-class Copyright © 2011 Greg Bulmash Licensed for use under Creative Commons 3.0 By http://creativecommons.org/licenses/by/3.0/ Must be attributed to Greg Bulmash of Brainhandles.com =============================================================*/ class bhHeatmap { private $dataset = array(); private $overlaypath = ""; private $overlayfile = ""; private $imagex = 0; private $imagey = 0; private $mainim = null; //placeholder, will become GD image object private $dotimage = null; //placeholder will become GD image object private $dotholder = array(); //placeholder will hold dot data for overlays private $imagespath = "/path/to/dir/with/dot.png"; private $dimsokay = false; private $arrayokay = false; private $overlayokay = true; public function makeMap(){ // after all the data is imported and set, this gets called // to process it and create the image if(!$this->allokay()) return false; // make blank white image with imagex/imagey dimensions; if(!$this->makeBaseImage()) return false; //overlay all the dots foreach ($this->dataset["points"] as $count => $points){ $this->setIntensity($count); foreach ($points as $notimportant => $point) { $this->overlayDot($point); } } // save base image imagejpeg($this->mainim, $this->overlaypath . "bw." . $this->overlayfile, 90); // colorize and save image $this->colorize(); } private function allokay(){ //returns true if the dimensions, overlay path, incoming array, and path to root images are okay. //throws errors if these data elements have not been properly specified if(!$this->dimsokay) trigger_error("Dimensons of output image were not properly specified. Heatmap image cannot be generated.",E_USER_ERROR); if(!$this->arrayokay) trigger_error("The array of data points was not properly imported. Image cannot be generated.",E_USER_ERROR); if(!$this->overlayokay) trigger_error("A viable path for saving the output image was not properly specified. Heatmap image cannot be generated.",E_USER_ERROR); if(!file_exists($this->imagespath . "dot.png")) trigger_error("The path to dot.png was not properly specified. Heatmap image cannot be generated.",E_USER_ERROR); return true; } public function setDims($x,$y){ // Set the dimensions of the output image in pixels. // Must be bigger than 50 x 50, but currently no upper limit. // Of course, the bigger the image, the bigger the memory and CPU use. $this->dimsokay = false; $isx = false; $isy = false; if(intval($x)>50){ $this->imagex = intval($x); $isx = true; } if(intval($y)>50){ $this->imagey = intval($y); $isy = true; } if(($isx)&&($isy)) $this->dimsokay = true; } public function setImagesPath($path){ // A dummy value is set in the object initialization. // You can hardcode the value above and never call this function // or you can pass the value from your script. The reason not to // hardcode it would be if you want to use this class with multiple // color schemes or dot shapes. if(!is_readable($path)) return false; if(!file_exists($path . "dot.png")) return false; $this->imagespath = $path; } public function setDimsFromPath($path){ // check that path resolves to a local image and set imagex, imagey for dimensions $this->dimsokay = false; $info = getimagesize($path); if(!info) return; if(($info[0]>50)&&($info[1]>50)){ $this->imagex = $info[0]; $this->imagey = $info[0]; $this->dimsokay = true; } } public function setOverlay($path, $filename){ // set the path and filename for the file you want // to write the heatmap overlay image to. $this->overlayokay = false; // check if directory exists and is writeable if(!is_writeable($path)) return false; //check if filename is okay if(!preg_match("/^[a-zA-Z0-9\-\.]+$/",$filename)) return false; $this->overlaypath = $path; $this->overlayfile = $filename; $this->overlayokay = true; } public function importData($datarray){ // does basic validation and processing of a clickstream: // input is an array of subarrays with the // subarrays having "x" and "y" integer values. It counts occurrences. $this->arrayokay = false; if(gettype($datarray) != "array") return false; if((!isset($datarray[0]["x"]))||(!isset($datarray[0]["y"]))) return false; $point = array("x"=>0,"y"=>0,"count"=>0); $pointset = array(); // reset array to empty foreach($datarray as $num=>$points){ // first ensure the point is within the boundaries of the image dimensions if(intval($points["x"]) < -14) continue; if(intval($points["x"]) > ($this->imagex + 14)) continue; if(intval($points["y"]) < -14) continue; if(intval($points["y"]) > ($this->imagey + 14)) continue; // create an entry for the point, if one does not exist, increment the count $wholepoint = intval($points["x"])."x".intval($points["y"]); if(!isset($pointset[$wholepoint])) { $pointset[$wholepoint] = $point; $pointset[$wholepoint]["x"] = intval($points["x"]); $pointset[$wholepoint]["y"] = intval($points["y"]); } $pointset[$wholepoint]["count"] += 1; } //Get max & min for intensity adjustments $this->generateMinMax($pointset); $this->arrayokay = true; return true; } public function setArray($datarray){ /* if you've already got your points and their relative values you structure an array like this.... $array["20x40"]["x"] = 20; $array["20x40"]["y"] = 40; $array["20x40"]["count"] = 18; */ // validate the array $this->arrayokay = false; $arrayisgood = true; foreach($datarray as $point=>$values){ // make sure $point is in the form of "20x40" if(!preg_match("/^[0-9]+x[0-9]+$/",$point)){ $arrayisgood = false; break; } // make sure $values contains x, y, and count values if((!isset($values["x"]))||(!isset($values["y"]))||(!isset($values["count"]))){ $arrayisgood = false; break; } // make sure the x, y, and count values are greater than 0 if((!intval($values["x"])>0)||(!intval($values["y"])>0)||(!intval($values["count"])>0)){ $arrayisgood = false; break; } } if($arrayisgood) { $this->arrayokay = true; } else { return false; } $this->generateMinMax($datarray); } private function generateMinMax($datarray) { $this->dataset = array("min"=>8000000000,"max"=>-8000000000,"points"=>array()); foreach($datarray as $point => $info){ $count = $info["count"]; if($this->dataset["min"] > $count) $this->dataset["min"] = $count; if($this->dataset["max"] < $count) $this->dataset["max"] = $count; //after determining if the incidence of the point should expand the min or max, we group the points by incidence in $dataset["points"] if(!isset($this->dataset["points"][$count])) $this->dataset["points"][$count] = array(); $this->dataset["points"][$count][] = array("x"=>$info["x"],"y"=>$info["y"]); } } private function setIntensity($density){ $top = $this->dataset["max"]; $bottom = $this->dataset["min"]; $range = 1 + ($top - $bottom); //range is (max - min) + 1 to get the true number of points in the range; $rangeplier = $range/55; if($range < 25) $rangeplier = $rangeplier * (55/($range*2)); $maxintensity = 70 - (58 - ceil(($range - 1)/$rangeplier)); $adjuster = ($maxintensity < 68) ? 67 - $maxintensity : 0; $realadjuster = ceil(($adjuster + ($adjuster * (1/(($range + 1) - ($density - $bottom)))))/2); $intensity = $realadjuster + (70 - (58 - ceil(($density - $bottom)/$rangeplier))); // now make the dot $im = imagecreatetruecolor(32,32); $white = imagecolorallocate($im, 255, 255, 255); imagefill($im,0,0,$white); $bol = imagecreatefrompng($this->imagespath . 'dot.png'); imagecopymergegray($im, $bol, 0, 0, 0, 0, 32, 32, $intensity); $dot = array ("r"=>0,"g"=>0,"b"=>0); for($y = 0; $y < 32; $y++){ $this->dotholder[$y] = array(); for($x = 0; $x < 32; $x++) { $this->dotholder[$y][$x] = $dot; $rgb = imagecolorat($im,$x,$y); $this->dotholder[$y][$x]["r"] = ($rgb >> 16) & 0xFF; $this->dotholder[$y][$x]["g"] = ($rgb >> 8) & 0xFF; $this->dotholder[$y][$x]["b"] = $rgb & 0xFF; } } } private function makeBaseImage(){ //creates a blank white image using the dimensions if(!$this->dimsokay) return false; $this->mainim = imagecreatetruecolor($this->imagex,$this->imagey); $white = imagecolorallocate($this->mainim, 255, 255, 255); imagefill($this->mainim, 0, 0, $white); return true; } private function overlayDot($points){ $dotsize = 32; // in future we can make this a settable value $xmax = $this->imagex; $ymax = $this->imagey; $topx = $points["x"] - intval($dotsize/2); $topy = $points["y"] - intval($dotsize/2); for($y = 0; $y < $dotsize; $y++){ for($x = 0; $x < $dotsize; $x++){ $inx = $topx + $x; $iny = $topy + $y; if(($inx < 0)||($iny < 0)) continue; if(($inx > ($xmax-1))||($iny > ($ymax-1))) continue; //get the color for the pixel $rgb = imagecolorat($this->mainim,$inx,$iny); $rbase = ($rgb >> 16) & 0xFF; $gbase = ($rgb >> 8) & 0xFF; $bbase = $rgb & 0xFF; // let's multiply this pixel by the corresponding dot pixel $rbase = intval(($rbase * $this->dotholder[$y][$x]["r"])/255); $gbase = intval(($gbase * $this->dotholder[$y][$x]["g"])/255); $bbase = intval(($bbase * $this->dotholder[$y][$x]["b"])/255); if(isset($rebase)) imagecolordeallocate($this->mainim, $rebase); // deallocate the $rebase color if it was allocated previously $rebase = imagecolorallocate($this->mainim, $rbase, $gbase, $bbase); // allocate the $rebase color with the multiplied value imagesetpixel($this->mainim, $inx, $iny, $rebase); // set the pixel to the newly multiplied value } } } private function colorize(){ $palcolors = $this->importColors($this->imagespath . 'colors.png'); $im = imagecreatefromjpeg($this->overlaypath . "bw." . $this->overlayfile); $height = imagesy($im); $width = imagesx($im); for($x = 0; $x < $width; $x++){ for($y = 0; $y < $height; $y++) { $rgb = imagecolorat($im,$x,$y); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; $col = 255 - intval(($r + $g + $b)/3); if(isset($newcol)) imagecolordeallocate($im, $newcol); $newcol = imagecolorallocate($im, $palcolors[$col]["r"], $palcolors[$col]["g"], $palcolors[$col]["b"]); imagesetpixel($im, $x, $y, $newcol); } } imagejpeg($im,$this->overlaypath . $this->overlayfile); } private function importColors($image){ $dotpallette = array(); $pixel = array("r"=>0,"g"=>0,"b"=>0); $sim = imagecreatefrompng($image); for($i = 0; $i < 256; $i++){ $dotpallette[$i] = $pixel; $rgb = imagecolorat($sim,16,$i); $dotpallette[$i]["r"] = ($rgb >> 16) & 0xFF; $dotpallette[$i]["g"] = ($rgb >> 8) & 0xFF; $dotpallette[$i]["b"] = $rgb & 0xFF; } return $dotpallette; } } ?>