Flex热点图客户端渲染

相信大家都用过用GIS做热点图的经历,那么通常的流程是,构建一个热点图工具,测试运行,在ArcMap中将工具添加到TOC中,然后发布一个GP服务并且带有ResultMap Service,这样在前端需要热点图的时候,发出请求,执行GP服务,返回生成的热地图服务,添加到客户端的MAP中。这样一个过程不仅复杂而且容易出错,同时响应慢,有时需要等待一段时间。

既然如此,下面就向大家介绍一种Flex客户端生成热点图的方法,通过客户端生成热点图,方便快捷。

原理:

1、针对空间座标的各个点,计算出屏幕座标,并保存在数组中。

2、利用BitmapData对象,在各个点的屏幕座标处,绘制圆形,圆形的大小可以根据当前点的相关属性进行设置。

3、对BitMapData绘制的图形进行处理,比如阀值处理,渐变处理,滤镜处理等。如下图:




客户端热点图渲染示例

实现步骤:

1、针对空间数据的热点图客户端渲染,利用ArcGIS API for Flex接口,通过自定义heatmap图层,并继承FeatureLayer,用于显示,获取数据。

public class HeatmapLayer extends FeatureLayer

2、计算FeatureLayer中点数据的屏幕座标并保存在数组中:

private function updatePoints():Boolean {
			m_x.length = 0;
			m_y.length = 0;
			
			var max:Number = 5.0;
			
			const mapW:Number = super.map.width;
			const mapH:Number = super.map.height;
			const extW:Number = super.map.extent.width;
			const extH:Number = super.map.extent.height;
			const facX:Number = mapW / extW;
			const facY:Number = mapH / extH;
			
			var weight:Number  = 1;
			
			const dict:Dictionary = new Dictionary(true);
			var ac:ArrayCollection = this.graphicProvider as ArrayCollection;
			for each (var g:Graphic in ac) {
				if (g.geometry.type != com.esri.ags.geometry.Geometry.MAPPOINT) {return false;}
				g.alpha = (m_showPoints) ? 1 : 0;
				if (super.map.extent.contains(g.geometry)) {
					const sx:Number = ((g.geometry as MapPoint).x - super.map.extent.xmin ) * facX;
					const sy:Number = mapH - ((g.geometry as MapPoint).y - super.map.extent.ymin ) * facY;
					
					m_x.push(sx);
					m_y.push(sy);
					
					const key:String = Math.round(sx) + "_" + Math.round(sy);
					var val:Number = dict[key] as Number;
					if (m_valueField != "") {
						weight = g.attributes[m_valueField] || 1;
					}
					if (isNaN(val)) {
						val = weight;
					} else {
						val += weight;
					}
					dict[key] = val;
					max = Math.max(max, val);
				}
			}
			m_centerValue = Math.max(19.0, 255.0 / max);
			return true;
		}

3、利用BitMapData绘制圆形,并对图像进行处理

private function drawHeatMap():void {
			const heatDiameter:int = _heatRadius * 2;  //热点的直径
			const matrix1:Matrix = new Matrix();           //转换矩阵
			trace("a="+matrix1.a.toString());
			trace("b="+matrix1.b.toString());
			trace("c="+matrix1.c.toString());
			trace("d="+matrix1.d.toString());
			trace("tx="+matrix1.tx.toString());
			trace("ty="+matrix1.ty.toString());
			matrix1.createGradientBox(heatDiameter, heatDiameter, 0, -_heatRadius, -_heatRadius);
			
			m_shape.graphics.clear();
			trace(m_centerValue.toString());
			//指定渐变填充参数,放射状填充,指定填充颜色
			m_shape.graphics.beginGradientFill(GradientType.RADIAL, [m_centerValue, 0], [1,1], [0,255], matrix1);  
			m_shape.graphics.drawCircle(0, 0, _heatRadius);   //画圆
			m_shape.graphics.endFill();   //按照指定的参数进行填充
			m_shape.cacheAsBitmap = true;   //缓存
			
			//创建一个完全透明的位图
//			trace("m_shape.width="+m_shape.width.toString());
//			trace("m_shape.height="+m_shape.height.toString());
			const bitmapDataShape:BitmapData = new BitmapData(m_shape.width, m_shape.height, true, 0x00000000);
			const matrix2:Matrix = new Matrix();
			//matrix2沿着横纵方向移动半径的距离
			matrix2.tx = _heatRadius;
			matrix2.ty = _heatRadius;
			
			//将之前定义的圆形(m_shape)绘制出来,并在横纵座标移动一个半径的距离
			bitmapDataShape.draw(m_shape, matrix2);
			
			//clip是与Map完全重合的一个矩形
			const clip:Rectangle = new Rectangle(0, 0, super.map.width, super.map.height);
			
//			trace(m_bitmapDataLayer.width.toString());
			if (m_bitmapDataLayer && m_bitmapDataLayer.width !== map.width && m_bitmapDataLayer.height !== map.height)
			{
				//释放用来存储 m_bitmapDataLayer 对象的内存
				m_bitmapDataLayer.dispose();
				m_bitmapDataLayer = null;
			}
			if (m_bitmapDataLayer === null)
			{
				//第一次运行,创建一个和map一样大小的全透明的BitMapData
				m_bitmapDataLayer = new BitmapData(map.width, map.height, true, 0x00000000);
			}
			//透明填充
			m_bitmapDataLayer.fillRect(clip, 0x00000000);
			const len:int = m_x.length;
			for (var i:int = 0; i < len; i++)
			{
				matrix2.tx = m_x[i] - _heatRadius;
				matrix2.ty = m_y[i] - _heatRadius;
				m_bitmapDataLayer.draw(bitmapDataShape, matrix2, null, BlendMode.SCREEN);
			}
			//释放bitmapDataShape的内存
			bitmapDataShape.dispose();
			
			// paletteMap leaves some artifacts unless we get rid of the blackest colors 
			m_bitmapDataLayer.threshold(m_bitmapDataLayer, m_bitmapDataLayer.rect, POINT, "<=", 0x00000003, 0x00000000, 0x000000FF, true);
			
			// Replace the black and blue with the gradient. Blacker pixels will get their new colors from
			// the beginning of the gradientArray and bluer pixels will get their new colors from the end. 
			m_bitmapDataLayer.paletteMap(m_bitmapDataLayer, m_bitmapDataLayer.rect, POINT, null, null, gradientArray, null);
			
			// This blur filter makes the heat map looks quite smooth.
			m_bitmapDataLayer.applyFilter(m_bitmapDataLayer, m_bitmapDataLayer.rect, POINT, m_blurFilter);
			
			graphics.clear();
			
			var matrix3:Matrix = new Matrix(1.0, 0.0, 0.0, 1.0, parent.scrollRect.x, parent.scrollRect.y);
			
			graphics.beginBitmapFill(m_bitmapDataLayer, matrix3, false, false);
			graphics.drawRect(parent.scrollRect.x, parent.scrollRect.y, map.width, map.height);
			graphics.endFill();
			
		}

4、把自定义的热点图图层像普通的图层一样加载到Map中就可以实现热地图的客户端渲染了。

通过以上介绍就可以不用服务端,在客户端实现热点图的渲染,非常简单,快捷方便。

还可以通过ArcGIS Flex帮助里面的示例去体验与了解热地图的客户端渲染,点此体验

同时为大家提供了HeatmapLayer的在线下载,点此下载



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章