65.9K
CodeProject 正在变化。 阅读更多。
Home

Flex 中的客户端热力图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (4投票s)

2010 年 9 月 9 日

CPOL

4分钟阅读

viewsIcon

56353

downloadIcon

810

这是一个快速且免费的客户端热图工具。

FixedZoom.PNG

引言

这是最快且免费的热图工具。 它也是一个客户端热图。 因此,如果用户数量增加,性能不会下降。 它主要基于 JavaScript 和 Flex。 我们可以使用 ASP.NET、PHP 或其他服务器端语言。 服务器端语言只设置点和其他配置给 JavaScript。 加载 Flash 需要一些时间,但加载后,它是最快的。

背景

我需要一个灵活且可扩展的热图应用程序。 快速 Google 搜索引导我找到 gheat。 我发现它很慢,并且不够灵活,无法满足我的需求。 我还找到了 GHeat.NET,它也很慢(在放大、缩小、拖放方面),如果用户数量增加,它的性能也会很差。 所以我决定尝试在客户端使用 Flex 制作热图工具。

Using the Code

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

它具有自动缩放和自动居中功能。 如果我们不设置缩放和中心值,它将确定缩放值和中心,以便显示所有点。 HeatMapData 方法将关于点的信息提供给 Flash。 在这里,allPoints 是字符串形式的点,例如 "21.400000,-157.797429#25.850000,-80.185260"。 下一个字段是缩放标签,如果它是 "",它将进行自动缩放。 下一个字段是地图的中心,如果它是 "",它将进行自动居中。 下一个字段是配色方案,如 classic、fire、omg、pbj、pgaitch。 如果它是 "",它将显示经典方案。 下一个字段是 gmap 类型,如果它是 "",它将显示普通地图。

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

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

使用 SetAllPoints 方法,服务器脚本将所有点的值设置为 JavaScript。 在 Flash 加载后,Flash 调用 JavaScript 方法 GetKey 并获取 gmap 密钥,然后 Flash 加载 gmap,在 gmap 准备好后,Flash 调用 JavaScript callHeatMapData 方法并获取所有点的信息。 然后 Flash 显示热图。

它是如何工作的?

加载 Flash 后,它设置加载 swf 的位置,然后调用 JavaScript 方法 GetKey() 获取 gmap 密钥。

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");
}

然后 JavaScript GetKey 方法使用 gmap 密钥调用 Flash MapLoad 方法。

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

Flash MapLoad 方法初始化 gmap 并添加 MapEvent.MAP_READY 事件。

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);
}

在 gmap 准备就绪后,它调用 JavaScript callHeatMapData 方法来获取点。

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");
    }

JavaScript callHeatMapData 方法然后使用所有信息调用 Flash HeatMapData 方法。

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

然后 Flash HeatMapData 方法设置 gmap 类型,然后设置屏幕颜色。 然后通过点找到 gmap 中的所有 LatLng。 然后它设置 gmap 的缩放和中心。 如果缩放和中心是自动的,那么它使用 LatLngBounds 来确定 gmap 的缩放和中心。 然后它添加 Mouse Down、Mouse Up 和 Mouse Move 事件侦听器。 然后它调用 InitialImage 方法,用于在 gmap 上显示屏幕图像。 然后它根据 gmap 缩放选择点图像。 然后它调用 GenareteImage 方法来显示热图图像。

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;
    }

InitialImage 方法采用一个 bitmapdata,其高度和宽度与 gmap 相似。 最初,它的所有像素颜色都是白色。 然后 bitmapdata 分成 50X50 的瓦片。 然后在所有瓦片中设置一个默认的 bitmapdata。 它使用一个 _flag 数组来识别哪些瓦片应该被更新。

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

GenareteImage 通过计时器调用另一个方法 ProcessForGenareteImageProcessForGenareteImage 方法找到所有 LatLng 的像素坐标,然后检查哪些点当前在 gmap 视图区域中,对于这些点,通过调用 CopyDot 方法来改变 _densityCointainer bitmapdata 中颜色的强度(更深)。 然后对于所有瓦片,检查哪些应该被更新。 如果一个瓦片应该被更新,那么它通过调用 Colorize 方法,根据配色方案绘制瓦片。

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);
	}
}

CopyDot 方法根据点图像改变 _densityCointainer bitmapdata 的强度。

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;
 }

Colorize 方法根据配色方案绘制更新的瓦片。

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;
 }

当发生 Mouse Down 事件时,它调用 InitialImage 方法来初始化场景。 然后当发生 Mouse Move 事件时,它移动 gmap。 然后当发生 Mouse Up 事件时,它调用 GenareteImage 方法来绘制热图。 在放大、缩小中,它改变 gmap 缩放,然后调用 InitialImageGenareteImage 方法来重新绘制热图。

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;
 }

当用户更改组合框配色方案时,JavaScript 方法 changeColorScheme 使用方案颜色名称调用 Flash 方法 ChangeColorScheme。 然后 Flash ChangeColorScheme 方法更改屏幕颜色 bitmapdata 并调用 InitialImageGenareteImage 方法,用于根据新的配色方案绘制热图。

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();
}

谢谢

感谢 gheatGHeat.NET

历史

  • 2010年9月9日:初始发布
© . All rights reserved.