Click here to Skip to main content
Click here to Skip to main content

Tagged as

Client Side Heat Map in Flex

, 16 Sep 2010
Rate this:
Please Sign up or sign in to vote.
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
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.

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.

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.

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

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

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.

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.

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.

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.

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.

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.

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.

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.

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 method for drawing the heat map according to the new color scheme.

function changeColorScheme(control) {
        getFlexApp('mySwf').ChangeColorScheme(control.value);
}
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)

Share

About the Author

shakil0304003
Software Developer (Senior) Astha IT Research & Consultancy Ltd.
Bangladesh Bangladesh
I am Committed to professionalism, eagerness to learn and has strong passion in software engineering.

Comments and Discussions

 
QuestionNeed to add marker on dots PinmemberNidhiKanu29-Jul-11 2:58 
AnswerRe: Need to add marker on dots Pinmembershakil030400329-Jul-11 7:18 
QuestionProgramming Environment Pinmemberdcasso127-Jun-11 22:06 
AnswerRe: Programming Environment Pinmembershakil030400320-Jul-11 1:25 
GeneralWants to add heat map in flex project PinmemberRavi0072317-May-11 22:34 
GeneralRe: Wants to add heat map in flex project Pinmembershakil030400317-May-11 22:44 
GeneralRe: Wants to add heat map in flex project PinmemberRavi0072318-May-11 7:05 
GeneralRe: Wants to add heat map in flex project Pinmembershakil030400318-May-11 7:13 
GeneralRe: Wants to add heat map in flex project PinmemberRavi0072318-May-11 18:27 
GeneralRe: Wants to add heat map in flex project Pinmembershakil030400318-May-11 18:58 
GeneralRe: Wants to add heat map in flex project PinmemberRavi0072318-May-11 21:59 
GeneralRe: Wants to add heat map in flex project Pinmembershakil030400318-May-11 22:09 
GeneralGreate article about heat map!!! Pinmembers.Crazy1-Feb-11 0:13 
GeneralRe: Greate article about heat map!!! Pinmembershakil03040031-Feb-11 21:42 
Questionwhere is java script code? PinmemberMember 350098725-Nov-10 7:46 
AnswerRe: where is java script code? [modified] Pinmembershakil030400324-Jan-11 23:48 
Generaldownloaded HeatMap.html doesn't loads the map PinmemberMember 350098726-Oct-10 5:13 
GeneralRe: downloaded HeatMap.html doesn't loads the map Pinmembershakil03040032-Nov-10 5:18 
GeneralRe: downloaded HeatMap.html doesn't loads the map PinmemberMember 350098725-Nov-10 6:58 
GeneralRe: downloaded HeatMap.html doesn't loads the map Pinmembershakil030400325-Jan-11 0:11 
GeneralIE problem Pinmemberaggelos@next14-Sep-10 5:38 
GeneralRe: IE problem Pinmembershakil030400314-Sep-10 20:33 
GeneralRe: IE problem Pinmembershakil030400314-Sep-10 20:58 
GeneralRe: IE problem Pinmemberaggelos@next15-Sep-10 0:35 
GeneralNeeds work PinmvpDave Kreskowiak9-Sep-10 3:41 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 17 Sep 2010
Article Copyright 2010 by shakil0304003
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid