相信大家都用過用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的在線下載,點此下載。