背景
热力图,在空间数据可视化场景中是一个非常常见的需求。首先看下一个完整的热力图效果。
前端实现热力图的原理可以看该博文。本文不是探究热力图前端实现原理的,是来探讨由于热力图一次性加载的点过多,产生卡顿的问题。
最后,为大家提供一个热力图的性能优化方法,由于热力图一次性加载的点过多,所以容易出现卡顿问题,而前端在渲染热力图时,可以进行热力图的点聚合优化。关于点聚合优化的实施方法:将视窗划分成为网格进行操作,由此判断热力图数据点在网格中所处的位置,如果同时几个点处于一个网格,则合并这几个点,以此降低渲染成本。
前面的博文也提出了自己的解决办法。但是若数据居多(10w+),光数据传输就头疼。现有的热力图的前端组件有很多,可以用openlayer、高德地图js、百度地图js、mapbox等。但是大多数的数据结构的都是下面这种形式。
所以需要在后台将下面策略实现,给前台页面传输合适的数据就行。
最后,为大家提供一个热力图的性能优化方法,由于热力图一次性加载的点过多,所以容易出现卡顿问题,而前端在渲染热力图时,可以进行热力图的点聚合优化。关于点聚合优化的实施方法:将视窗划分成为网格进行操作,由此判断热力图数据点在网格中所处的位置,如果同时几个点处于一个网格,则合并这几个点,以此降低渲染成本。
计算热力图中bucket的方法
width_bucket(operand dp, b1 dp, b2 dp, count int)
int
return the bucket number to which operand would be assigned in a histogram having count equal-width buckets spanning the range b1 to b2;
returns 0 or count+1 for an input outside the range
width_bucket(5.35, 0.024, 10.06, 5)
3
width_bucket(operand numeric, b1 numeric, b2 numeric, count int)
int
return the bucket number to which operand would be assigned in a histogram having count equal-width buckets spanning the range b1 to b2;
returns 0 or count+1 for an input outside the range
width_bucket(5.35, 0.024, 10.06, 5)
3
生成数据
SELECT ST_SetSRID(ST_MakeBox2D(ST_Point(119.7342, 29.9602),ST_Point(120.6181 ,30.5581)),4326) geom;
--生成热力图点
CREATE TABLE heatmap_points AS SELECT
(
ST_Dump (
ST_GeneratePoints (kl.geom, 200000)
)).geom AS geom,md5((random()*random())::text) as id,random()*1000 as val
FROM
(
SELECT
ST_SetSRID (
ST_MakeBox2D (
ST_Point (119.7342, 29.9602),
ST_Point (120.6181, 30.5581)
),
4326
) geom
) kl;
点位聚合
with tt as (select
width_bucket(st_x(geom), 119.7342, 120.6181, 50) grid_x,
width_bucket(st_y(geom), 29.9602, 30.5581, 50) grid_y,
geom,
val
from heatmap_points)
SELECT avg(val),min(val), max(val), stddev(val),st_centroid(st_collect(geom)) geom from tt GROUP BY tt.grid_x,grid_y
小结
借助postgresql的width_bucket和postgis,仅用0.4s将20W的数据压缩到2500,这就大大降低了数据传输和渲染的压力。同时也不需要对已有的GIS前端热力图组件进行修改。