相信大家都用过用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的在线下载,点此下载。