作者:小阳
###一、概述
如今大数据时代的到来,数据可视化在人们的日常生活中扮演着越来越重要的角色,当然,热点图也是当中流行的一种数据可视化表达的手段之一。那什么是热点图呢?——热点图通常将一些不易理解或表达的数据,比如密度,频率,温度等,用区域和颜色这种更容易被人理解的方式来呈现的一种数据可视化手段。在热点图中,暖色一般代表事件发生频率比较高或事物分布密度比较大的区域,冷色反之。那热点图又有什么作用呢?—热点图能够明显的表现地图中事件发生的频率或者事物的分布密度。
既然本文讲述的是热点图,那为什么要加上时间轴呢?因为时间轴可以控制不同时间的不同显示状态,加上时间轴,就可以显示不同时间点的热力图了。下面我以某市区一天的五个时间点的景区人流量为例,对本范例做相关介绍和实现思路,详见后文。
###二、数据准备
热点数据是整个热点图的核心,它是一个包含了坐标信息,时间信息的点对象集合。在这里我们将所有热点用一个点数据集HeatPoints来存储:
并在为HeatPoints一个日期字段TIMEINDEX,字段类型为日期或文本,本范使用的字段类型为日期:
数据准备完后前端要对该数据集进行查询,可使用如下两个方案对数据进行发布。如果没有数据服务的许可,只能使用方案二。
方案一:将工作空间发布为或REST-地图服务和REST-数据服务(推荐)
方案二:将HeatPoints叠加到地图中,并设置为不可见,再将工作空间发布为REST-地图服务。
###三、开发要点
本范例中使用的是方案一的发布方式。实现的思路是采用数据集查询的方式对数据进行查询,将查询到的featrues绑定到TimeSlider上,随着时间轴的切换对featurelayer进行监听,获取不同时间点featurelayer的featrues,并转换为heatpoints加载到HeatMapLayer上。
####1、 添加相关组件
<ic:Map id="map" >
<ic:TiledDynamicRESTLayer url="{this.restUrl}" tileSize="256"/>
<ic:FeaturesLayer id="fl" isFront="false" isFeatureMouseOver="false" alpha="0" updateEnd="fl_updateEndHandler(event)"/>
<ic:HeatMapLayer id="heatMap" radius="20" alpha="0.9" heatStops="{SetHeatStops()}"/>
</ic:Map>
<ic:TimeSlider id="timeSlider" bottom="20" width="387" layer="{fl}"
timeChange="timeSlider_timeChangeHandler(event)"
creationComplete="timeSlider_creationCompleteHandler(event)"
horizontalCenter="0" isAccumulatedDisplay="false" />
<s:Label id="timeLabel" fontFamily="微软雅黑" fontSize="14" fontWeight="bold" color="0XFFFFCC" horizontalCenter="-23" bottom="50" />
TiledDynamicRESTLayer在这里作为地图底图的加载与显示,可能有人会问:那么FeaturesLayer是做什么的呢,用过时间轴(即TimeSlider)的开发人员应该都知道,TimeSlider 有一个 layer属性用来绑定用于显示要素的FeatureLayer或者是GraphicsLayer,由于TimeSlider不能直接绑定HeatMapLayer(即热点图层),这里就用FeatureLayer绑定TimeSlider,并且FeatureLayer的透明度为0,即不可见,这样做的目的是为了获取时间轴在不同时间点的热点数据,在这里FeatureLayer实质上只起到了一个数据的临时存储作用,如果数据量相对来说比较大的话,请使用GraphicsLayer,这一点很关键。
####2、事件函数
热点数据的SQL查询
private function init():void
{
restUrl="http://192.168.15.99:8090/iserver/services/map-heatmap/rest/maps/heatmap_night";
dataUrl="http://192.168.15.99:8090/iserver/services/data-heatmap/rest/data/featureResults";
var queryParams:Array = ["Changchun:HeatPoint"];
var queryBySQLParameters:GetFeaturesBySQLParameters = new GetFeaturesBySQLParameters();
queryBySQLParameters.datasetNames = queryParams;
queryBySQLParameters.toIndex=-1;
var filterParameter:FilterParameter = new FilterParameter();
filterParameter.attributeFilter = "SMID>0";
queryBySQLParameters.filterParameter = filterParameter;
var getFeaturesBySQL:GetFeaturesBySQLService = new GetFeaturesBySQLService(this.dataUrl);
getFeaturesBySQL.processAsync(queryBySQLParameters, new AsyncResponder(this.dispalyQueryRecords,excuteErros,null));
}
获取时间轴的要素数据
feature.attributes.TIMEINDEX=DateFormatter.parseDateString(feature.attributes.TIMEINDEX); 这行代码很关键,因为SQL查询出来的feature对象TIMEINDEX属性为字符型,而timeslider需要使用Date型的对象,所以需要将该属性重新进行对象转换。
private function dispalyQueryRecords(result:GetFeaturesResult, mark:Object):void
{
var features:Array = result.features;
if(features.length == 0)
{
Alert.show("查询结果为空", "提示", 4, this);
return;
}
else
{
for each(var feature:Feature in result.features)
{
feature.attributes.TIMEINDEX=DateFormatter.parseDateString(feature.attributes.TIMEINDEX); //转字符为时间对象
this.timeData.features.addItem(feature);
}
}
var timeStops:Array = new Array();
for(var i:int = 0; i < 5; i++)
{
var date:Date = new Date(2015,0,1,6+i*3,0,0);
timeStops.push(date);
}
var startTime:Date = new Date(2015,0,1,6,0,0); //月份从0开始
timeData.timeStops = timeStops;
var endTime:Date = new Date(2015,0,1,18,0,0);
timeData.startTime = startTime;
timeData.endTime = endTime;
timeData.timeField="TIMEINDEX";
timeSlider.timeData=this.timeData;
}
颜色过渡站点的设置
设置HeatMapLayer热点圆渐变填充中的颜色过渡站点(HeatStop)集合。其中站点的信息包括位置和颜色。
private function SetHeatStops():Array
{
var setHeatStops:Array=[];
setHeatStops.push(new HeatStop(0XFF0000,1));
setHeatStops.push(new HeatStop(0XFF6600,0.98));
setHeatStops.push(new HeatStop(0XFFFF00,0.8));
setHeatStops.push(new HeatStop(0X33FF33,0.5));
setHeatStops.push(new HeatStop(0X6633CC,0.1));
return setHeatStops;
}
时间轴的要素数据转换为热点(heatpoint)
由于热点图层中的加载的为heatpoint对象,在做时间轴切换的时候,相应的点是以feature的形式加载在featurelayer上面的,在这里,我们的思路是:找到时间轴的一个监听事件,在做时间切换的时候可以触发一个函数,可以获取到此时featurelayer上的feature集合,将featurelayer上的features临时转换为heatpoint集合,这样就实现了一个热点数据的获取。答案是肯定的。
在这里,我们需要在featurelayer上写updateEnd事;同时还要在TimeSlider
上写timechange事件,目的是为了保证在时间轴进行切换的时候,能够及时触发featurelayer的刷新。
protected function fl_updateEndHandler(event:LayerEvent):void
{
heatpoints=[];
for each(var feature:Feature in fl.features)
{
heatpoints.push(new HeatPoint(feature.geometry.center.x,feature.geometry.center.y));
}
heatMap.heatPoints=heatpoints;
}
protected function timeSlider_timeChangeHandler(event:TimeSliderEvent):void
{
fl.refresh();
timeLabel.text=dateFormatter.format(this.timeSlider.timeStops[this.timeSlider.slider.value]);
}
最后的实现效果图如下: