Click here to Skip to main content
15,867,756 members
Articles / Programming Languages / Javascript

Client Side Heat Map in Flex

Rate me:
Please Sign up or sign in to vote.
4.67/5 (4 votes)
16 Sep 2010CPOL4 min read 53.2K   793   14   29
This is a fast and free client side heat mapping tool.
FixedZoom.PNG

Introduction

This is the fastest & free heat mapping tool. It is a client side heat map too. So, if the number of users increase, performance will not drop. It is mainly based on JavaScript & Flex. WE can use ASP.NET, PHP or other server site languages. The server site language only sets points & other configuration to the JavaScript. For loading the flash, it takes some time, but after the load, it is the fastest.

Background

I had the need for a flexible and scalable heat mapping application. A quick Google search lead me to gheat. I found it to be slow and not flexible enough to suit my needs. I also found GHeat.NET which is also slow(in zoom in, zoom out, drag, drop) and if the number of users increase, then its performance is too bad. So I decided to attempt to make heat map tools in client side and using flex.

Using the Code

AutoZoom.PNG
JavaScript
function callHeatMapData() {
getFlexApp('mySwf').HeatMapData(allPoints,"","","","");
//getFlexApp('mySwf').HeatMapData
//	(allPoints,"30.1238660,-92.0706730","5","fire","hybrid");
}

It has auto zoom & auto center functionality. If we do not set zoom & center value, then it will determine the zoom value & center for all the points showable. HeatMapData method gives the flash about the points information. Here, allPoints are the points in string, like this "21.400000,-157.797429#25.850000,-80.185260". The next field is zoom label, if it is "", then it will make auto zoom. The next field is center of the map, if it is "", then it will make auto center. The next field is color scheme, like classic, fire, omg, pbj, pgaitch. If it is "", then it will show classic scheme. The next field is gmap type, if it is "", then it will show normal map.

JavaScript
var allPoints="";
var key = "Key";
    
function SetAllPoints(value)
{
    allPoints = value;
}

function getFlexApp(appName) {
    return window.document[appName];
}
    
function GetKey()
{
    getFlexApp('mySwf').MapLoad(key);
}

Using SetAllPoints method, server script sets all points value to the JavaScript. After the flash load, flash calls the JavaScript method GetKey and gets the gmap key, then flash loads the gmap & after the gmap is ready, flash calls JavaScript callHeatMapData method and gets all points information. Then flash shows the heat map.

How Does it work?

After loading the flash, it set the loading swf position, then it call the JavaScript method GetKey() for get the gmap key.

JavaScript
public function OnLoad():void
{
 	swfLoader.x = (this.width - swfLoader.width)/2;
 	swfLoader.y = (this.height - swfLoader.height)/2;
 	ExternalInterface.addCallback("MapLoad", MapLoad);
 	ExternalInterface.call("GetKey");
}

Then JavaScript GetKey method calls the flash MapLoad method with the gmap key.

JavaScript
function GetKey()
{
    getFlexApp('mySwf').MapLoad(key);
}

The flash MapLoad method initializes the gmap and adds MapEvent.MAP_READY event.

JavaScript
public function MapLoad(key:String):void
{
	map = new Map();
 	map.setStyle("left","0");
 	map.setStyle("right","0");
 	map.setStyle("top","0");
 	map.setStyle("bottom","0");
 	map.id = "map";
 	map.key = key;
 	map.addEventListener(MapEvent.MAP_READY,onMapReady);
 	cnsGoogleMap.addChild(map);
}

After the gmap is Ready, it calls the JavaScript callHeatMapData method for getting the points.

JavaScript
private function onMapReady(event:Event):void {
     
     if (ExternalInterface.available)
     {
     ExternalInterface.addCallback("HeatMapData", HeatMapData);
     ExternalInterface.addCallback("ChangeColorScheme", ChangeColorScheme);
     }
     else
     Alert("fail");
       
     mapType = new ArrayCollection();
     mapType.addItem("Normal");
     mapType.addItem("Hybrid");
     mapType.addItem("Physical");
     mapType.addItem("Satellite");
     cmbMapType.dataProvider = mapType;
     
     map.setMapType(MapType.NORMAL_MAP_TYPE);
    
     ExternalInterface.call("callHeatMapData");
    }

The JavaScript callHeatMapData method then calls the flash HeatMapData method with all information.

JavaScript
function callHeatMapData() {
        getFlexApp('mySwf').HeatMapData(allPoints,"","","","");
        //getFlexApp('mySwf').HeatMapData(allPoints,
        //	"30.1238660,-92.0706730","5","fire","hybrid");
    }

Then the Flash HeatMapData method sets the gmap type, then screen color. Then finds all LatLng from the gmap by the points. Then it sets zoom & center of the gmap. If the zoom & center is auto, then it uses LatLngBounds for determining the zoom & center of the gmap. Then it adds Mouse Down, Mouse Up & Mouse Move event listener. Then it calls InitialImage method for displaying a screen image over the gmap. Then it chooses dots image according to the gmap zoom. Then it calls the GenareteImage method for showing the heat map image.

JavaScript
public function HeatMapData
    (allLatLng:String,center:String,zoom:String,
	colorScreen:String,mapType:String):void
     {   
     
     if(mapType.toLowerCase()=="normal")
     {
     map.setMapType(MapType.NORMAL_MAP_TYPE);
     cmbMapType.selectedIndex = 0;
     }
     else if(mapType.toLowerCase()=="hybrid")
     {
     map.setMapType(MapType.HYBRID_MAP_TYPE);
     cmbMapType.selectedIndex = 1;
     }
     else if(mapType.toLowerCase()=="physical")
     {
     map.setMapType(MapType.PHYSICAL_MAP_TYPE);
     cmbMapType.selectedIndex = 2;
     }
     else if(mapType.toLowerCase()=="satellite")
     {
     map.setMapType(MapType.SATELLITE_MAP_TYPE);
     cmbMapType.selectedIndex = 3;
     }
        	 	
     _screenColor = colorSchemesBitmapData.GetSchemesBitmapData
	(colorScreen.toLowerCase());//Bitmap(imgScheme.content).bitmapData;
 	 var hmo:HeatMapOpacity = new HeatMapOpacity();
     _allZoomOpacity = hmo.BuildZoomMapping(); 
     
     var allPoints:String = allLatLng;
     var points:Array = allPoints.split( '#' );
     
     var bounds:LatLngBounds = new LatLngBounds();
     _allLen = new ArrayCollection();
     
     for ( var i:int = 0; i < points.length; i++ ) {
          	var temp:Array = points[i].toString().split(',');
          	  
          	var latlng:LatLng = new LatLng(Number(temp[0]) ,Number(temp[1]));
  		if(latlng != null)
  		{
 			bounds.extend(latlng);
 			_allLen.addItem(latlng);
     	}
     }
    
     if(zoom==""||zoom == null)
     map.setZoom(map.getBoundsZoomLevel(bounds));
     else
     map.setZoom(Number(zoom));
     
     if(center==""||center==null)
     map.setCenter(bounds.getCenter());
     else
     {
     	var temp:Array = center.split(',');
        var latlng:LatLng = new LatLng(Number(temp[0]) ,Number(temp[1]));
        map.setCenter(latlng);
     }
     
     cnsShow.addEventListener(MouseEvent.MOUSE_DOWN,DragMap);
     cnsShow.addEventListener(MouseEvent.MOUSE_UP,DropMap);
	 cnsShow.addEventListener(MouseEvent.MOUSE_MOVE,MouseMove);
	 ImageGenaretorManager.Instance.InitialImage();
  	 _dotsColor = dotsBitmapData.GetDotBitmapData(getZoom);
	 ImageGenaretorManager.Instance.GenareteImage(); 
	 _mapShown = true;
    }

The InitialImage method takes a bitmapdata whose height & width is similar to the gmap. Initially all its pixel color is white. Then bitmapdata divides into 50X50 tiles. Then sets a default bitmapdata in all the tiles. It uses a _flag array for identifying which tiles should be updated.

JavaScript
public function InitialImage():void
{
	if(_timer != null && _timer.running == true)
	_timer.stop();
	_timer = null;
			
	Application.application.RemoveAllTilesFromHeatMapCanvas();
   _startX =	Application.application.HeatMapCanvas.x;
   _startY =	Application.application.HeatMapCanvas.y;
   _width = Application.application.HeatMapCanvas.width;
   _height = Application.application.HeatMapCanvas.height;
   _rowTilesNumber = Math.ceil(_width/TilesSize);
		   
   _densityCointainer =  
	new BitmapData(_width,_height,false,0xffffff); 
		   
   var currentX:Number = _startX;
   var currentY:Number = _startY;
		  
   var zoomLavel:Number = Application.application.ZoomOpacity
	[Math.floor(Application.application.getZoom)] as Number;   	
		   
   var tempBitmapData:BitmapData = new BitmapData
	(TilesSize,TilesSize,false,
	Application.application.ColorSchemeBitmapData.getPixel(0,255));
   var count:int = 0;
		   	   
   while(currentY<_height)
   {
   	var wid:Number = TilesSize;
   	var hei:Number = TilesSize;
   	
   	if(currentX+wid>_width)
   	wid = _width - currentX;
		   	
   	if(currentY+hei>_height)
   	hei = _height - currentY;
		   	
    var tempBitmap:Bitmap = new Bitmap(tempBitmapData);
    tempBitmap.alpha = zoomLavel/255;
    tempBitmap.height = hei;
    tempBitmap.width = wid;
    tempBitmap.x = 0;
    tempBitmap.y = 0;
    var uiHolder:UIComponent = new UIComponent();
    var bitmapHolder:Sprite = new Sprite();
    uiHolder.addChild(bitmapHolder);
    bitmapHolder.addChild(tempBitmap);
    uiHolder.x = currentX;
    uiHolder.y = currentY;
    uiHolder.name = "Tiles"+count.toString();
   	Application.application.AddToTheHeatMapCanvas(uiHolder);
   	count++;
   	currentX += TilesSize;
   	if(currentX>_width)
   	{
   		currentX = _startX;
   		currentY += TilesSize;
   	}
   }
		   
   _flag = new Array();
   for(var i:int =0;i

The GenareteImage calls another method ProcessForGenareteImage by timer. The ProcessForGenareteImage method finds the pixel co-ordinate for all LatLng, then checks which points are currently in the gmap view area and for those points, changes the intensity of the color (Darker) in the _densityCointainer bitmapdata by call the CopyDot method. Then for all the tiles, check which should be updated. If a tile should be updated, then it draws the tiles according to the color scheme by calling the Colorize method.

JavaScript
public function GenareteImage():void
{ 	
   _timer = new Timer(10);
   _timer.addEventListener(TimerEvent.TIMER, ProcessForGenareteImage);
   _timer.start();	   
}
		
private function ProcessForGenareteImage(evt:TimerEvent):void
{
   _timer.stop();
   
   var dotBitmap:BitmapData = Application.application.DotBitmapData; 
	   
   for each(var latleg:LatLng in Application.application.AllLen)
	{
		var point:Point =  Application.application.GetPointByLetLng(latleg);
		if(point.x>=_startX-dotBitmap.width/2&&point.y>=
					_startY-dotBitmap.height/2
		&&point.x<=_width+dotBitmap.width/2&&point.y<=
					_height+dotBitmap.height/2)
		{	
			_densityCointainer = ProcessImage.Instance.CopyDot
				(_densityCointainer,dotBitmap,point.x,point.y);
		}
	}
		
	var colorBitmap:BitmapData = Application.application.ColorSchemeBitmapData; 
	var zoomLavel:Number = Application.application.ZoomOpacity
			[Math.floor(Application.application.getZoom)] as Number;
	var tempBitmapData:BitmapData = new BitmapData
			(TilesSize,TilesSize,false,colorBitmap.getPixel(0,255));
			
	for(var i:int = 0; i<_flag.length;i++)
	{
		Application.application.RemoveTilesFromHeatMapCanvas
						("Tiles"+i.toString());
		var wid:Number = TilesSize;
		var hei:Number = TilesSize;
	   	
		if((i%_rowTilesNumber)*TilesSize+wid>_width)
		wid = _width - (i%_rowTilesNumber)*TilesSize;
	   	
		if(Math.floor(i/_rowTilesNumber)*TilesSize+hei>_height)
		hei = _height - Math.floor(i/_rowTilesNumber)*TilesSize;
	   		
		var bitmapColor:BitmapData = null;
	   		
		if((_flag[i] as Boolean) == true)
		bitmapColor = ProcessImage.Instance.Colorize
			(_densityCointainer,colorBitmap,(i%_rowTilesNumber)*
			TilesSize,Math.floor(i/_rowTilesNumber)*TilesSize,wid,hei);
		else
		bitmapColor = tempBitmapData;
			
		var tempBitmap:Bitmap = new Bitmap(bitmapColor);
		tempBitmap.alpha = zoomLavel/255;
		tempBitmap.height = hei;
	  	tempBitmap.width = wid;
   		tempBitmap.x = 0;
  		tempBitmap.y = 0;
    		var uiHolder:UIComponent = new UIComponent();
		var bitmapHolder:Sprite = new Sprite();
 		uiHolder.addChild(bitmapHolder);
		bitmapHolder.addChild(tempBitmap);
		uiHolder.x = (i%_rowTilesNumber)*TilesSize;
		uiHolder.y = Math.floor(i/_rowTilesNumber)*TilesSize;
		uiHolder.name = "Tiles"+i.toString();
		Application.application.AddToTheHeatMapCanvas(uiHolder);
	}
}

The CopyDot method changes the intensing of _densityCointainer bitmapdata according to the dot image.

JavaScript
public function CopyDot
	(source:BitmapData, destination:BitmapData,x:int,y:int):BitmapData
 {
 	for(var i:int = x-destination.width/2;  i < x - destination.width / 2 + 
			destination.width; i++)
 	for(var j:int = y-destination.height/2; j < y - destination.height / 2 + 
			destination.height; j++)
 	if(i >= 0 && j >= 0 && i < source.width && j < source.height)
 	{
 	    	var color:uint = destination.getPixel( i - 
		( x - destination.width / 2 ),j - ( y - destination.height / 2));
     		var color1:uint = source.getPixel(i,j);
 		    
     		var co1:uint = color%256;
 		var co2:uint = color1%256;
 	 	var newColor:uint = 0;
 		    	
 	    	if(co1!=255&&co2!=255)
 	    	{
 	    	var t1:Number = ((co1 as Number));
 	    	var t2:Number = ((co2 as Number));
 	    	t1 = (t1 * t2 )/ 255.0;
 	    	co1 = Math.floor(t1);
 	    	}
 		    	
 	    	ImageGenaretorManager.Instance.SetFlag(i,j);
 	    	newColor = co1 * 256 * 256 + co1 * 256 + co1;
 		    	
  		source.setPixel(i,j,newColor);
 	}		
 	return source;
 }

The Colorize method draws the updated tiles according to the color scheme.

JavaScript
public function Colorize(source:BitmapData, colorScheme:BitmapData,
	x:int,y:int,w:int,h:int):BitmapData
 {
 	var newBitmap:BitmapData = new BitmapData(w,h,false,0xffffff);
 			
 	for(var i:int = x; i < x + w ; i++)
 	for(var j:int = y; j < y + h;j++)
 	{
 		var c1:uint = source.getPixel(i,j);
 		c1 = c1 % 256;
 				
 		var c2:uint = colorScheme.getPixel(0,c1);
 		newBitmap.setPixel(i-x,j-y,c2);
 	}
 			
 	return newBitmap;
 }

When Mouse Down event occurs, then it calls InitialImage method for initializing the scene. Then when Mouse Move event occurs, it moves the gmap. Then when Mouse up event occurs, it calls the GenareteImage method for drawing the heat map. In Zoom In, Zoom Out, it changes the gmap zoom, then calls the InitialImage & GenareteImage method for redrawing the heat map.

JavaScript
private function ZoomIn(evt:MouseEvent):void
{
	map.zoomIn();
	_dotsColor = dotsBitmapData.GetDotBitmapData(getZoom);
	ImageGenaretorManager.Instance.InitialImage();
	ImageGenaretorManager.Instance.GenareteImage();
}
	
private function ZoomOut(evt:MouseEvent):void
{
	map.zoomOut();
	_dotsColor = dotsBitmapData.GetDotBitmapData(getZoom);
	ImageGenaretorManager.Instance.InitialImage();
	ImageGenaretorManager.Instance.GenareteImage();	
}
 
private function Click(evt:TimerEvent):void
{
	timer.stop();
	timer = null;
}
 	
private function DoubleClick(evt:MouseEvent):void
{
	if(timer != null)
	{
	_dragPoint = null;
	timer.stop();
	timer = null;
	map.zoomIn();
	_dotsColor = dotsBitmapData.GetDotBitmapData(getZoom);
	ImageGenaretorManager.Instance.InitialImage();
	ImageGenaretorManager.Instance.GenareteImage();
	}
	else
	{
	timer = new Timer(duration);
	timer.addEventListener(TimerEvent.TIMER,Click);
	timer.start();
	}
}
	
private function ReSize():void
{
 	if(_mapShown==true)
 	{
 	ImageGenaretorManager.Instance.InitialImage();
 	ImageGenaretorManager.Instance.GenareteImage();
 	}
 }
	
 private function DragMap(event:MouseEvent):void
 {
 	_dragPoint = new Point(event.stageX,event.stageY);
 	ImageGenaretorManager.Instance.InitialImage();
 	DoubleClick(event);
 }
 	
 private function MouseMove(event:MouseEvent):void
 {
 	if(_dragPoint != null)
 	{
 	var pointCenter:Point = GetPointByLetLng(map.getCenter());
 	pointCenter.x += (_dragPoint.x-event.stageX);
 	pointCenter.y += (_dragPoint.y-event.stageY);
 	var latlet:LatLng =  map.fromViewportToLatLng(pointCenter);
 	map.setCenter(latlet);
 	_dragPoint = new Point(event.stageX,event.stageY);
 	}
 }
 	
 private function DropMap(event:MouseEvent):void
 {		
 	if(_dragPoint != null)
 	{
 	ImageGenaretorManager.Instance.GenareteImage();
 	}
 	_dragPoint = null;
 }

When the user changes the combo box color scheme, then the JavaScript method changeColorScheme calls the flash method ChangeColorScheme with the scheme color name. Then the flash ChangeColorScheme method changes the screen color bitmapdata & calls the InitialImage & GenareteImage <code>method for drawing the heat map according to the new color scheme.

JavaScript
function changeColorScheme(control) {
        getFlexApp('mySwf').ChangeColorScheme(control.value);
}
JavaScript
public function ChangeColorScheme(value:String):void
{
 	_screenColor = colorSchemesBitmapData.GetSchemesBitmapData
	(value.toLowerCase());
	ImageGenaretorManager.Instance.InitialImage();
	ImageGenaretorManager.Instance.GenareteImage();
}

Thanks

Thanks to gheat & GHeat.NET.

History

  • 9th September, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) TigerIT Bangladesh Ltd.
Bangladesh Bangladesh
I am Committed to professionalism, eagerness to learn and has strong passion in software engineering.

Comments and Discussions

 
GeneralRe: Wants to add heat map in flex project Pin
shakil030400318-May-11 18:58
shakil030400318-May-11 18:58 
GeneralRe: Wants to add heat map in flex project Pin
Ravi0072318-May-11 21:59
Ravi0072318-May-11 21:59 
GeneralRe: Wants to add heat map in flex project Pin
shakil030400318-May-11 22:09
shakil030400318-May-11 22:09 
GeneralGreate article about heat map!!! Pin
s.Crazy1-Feb-11 0:13
s.Crazy1-Feb-11 0:13 
GeneralRe: Greate article about heat map!!! Pin
shakil03040031-Feb-11 21:42
shakil03040031-Feb-11 21:42 
Questionwhere is java script code? Pin
Member 350098725-Nov-10 7:46
Member 350098725-Nov-10 7:46 
AnswerRe: where is java script code? [modified] Pin
shakil030400324-Jan-11 23:48
shakil030400324-Jan-11 23:48 
Generaldownloaded HeatMap.html doesn't loads the map Pin
Member 350098726-Oct-10 5:13
Member 350098726-Oct-10 5:13 
GeneralRe: downloaded HeatMap.html doesn't loads the map Pin
shakil03040032-Nov-10 5:18
shakil03040032-Nov-10 5:18 
GeneralRe: downloaded HeatMap.html doesn't loads the map Pin
Member 350098725-Nov-10 6:58
Member 350098725-Nov-10 6:58 
GeneralRe: downloaded HeatMap.html doesn't loads the map Pin
shakil030400325-Jan-11 0:11
shakil030400325-Jan-11 0:11 
GeneralIE problem Pin
Aggelos.K.14-Sep-10 5:38
Aggelos.K.14-Sep-10 5:38 
GeneralRe: IE problem Pin
shakil030400314-Sep-10 20:33
shakil030400314-Sep-10 20:33 
GeneralRe: IE problem Pin
shakil030400314-Sep-10 20:58
shakil030400314-Sep-10 20:58 
GeneralRe: IE problem Pin
Aggelos.K.15-Sep-10 0:35
Aggelos.K.15-Sep-10 0:35 
GeneralNeeds work Pin
Dave Kreskowiak9-Sep-10 3:41
mveDave Kreskowiak9-Sep-10 3:41 
GeneralRe: Needs work Pin
shakil03040039-Sep-10 4:00
shakil03040039-Sep-10 4:00 
GeneralRe: Needs work Pin
shakil03040039-Sep-10 9:00
shakil03040039-Sep-10 9:00 
GeneralRe: Needs work Pin
Dave Kreskowiak9-Sep-10 13:16
mveDave Kreskowiak9-Sep-10 13:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.