PGIS大数据量点位显示方案
问题描述
PGIS在地图上显示点位信息时,随点位数量的增加浏览器响应速度会逐渐变慢,当同时显示上千个点时浏览器会变得非常缓慢,以下是进行的测试:
测试环境:
- 服务器:
CPU:Intel(R) Xeon(R) 2.00GHz ,4核
内存:2G
- 客户端:
CPU: E5200@ 2.59GHz, 2核
内存:2G
下面是测试结果:
点位数量 |
IE内存占用 |
平均响应时间 |
放大缩小 |
点击响应时间 |
100 |
44.22M |
基本无延时 |
1 S |
基本无延时 |
400 |
49.09M |
0.1 S |
1.5 S |
基本无延时 |
900 |
62.67M |
0.3 S |
3 S |
基本无延时 |
1500 |
67.85M |
1.5 S |
5 S |
基本无延时 |
10000 |
164.104M |
不能拖动 |
不能缩放 |
原因分析
以下是从PGIS显示原理和平台代码实现方式两个方面进行的分析。
PGIS显示原理
山海经纬PGIS利用微软的VML技术在地图上描绘一个点位,每个点位信息最终都生成一个div图层,覆盖到地图图层上。当同时显示上千个点时,浏览器要同时渲染上千个div图层,非常影响性能。
平台显示点位信息的方法
平台在批量显示点位信息时,分3个步骤进行:
- 首先查询平台业务数据库,获得安装点编号的集合。
- 根据安装点编号集合拼接查询条件,到PGIS空间数据库中查询需要显示的安装点集合。
- 循环PGIS查询出来的安装点集合,显示到地图上。在循环中同时查询业务数据库,获得每个安装点的详细信息,作为弹出信息窗的内容。
现行做法存在的问题
- 业务数据库中没有存安装点的坐标,需要同时查询业务库和PGIS空间库才能将安装点显示出来。
- 安装点明细信息没有进行缓存,每显示一个安装点都需要查询一次业务数据库。
- 一次将所有点位显示出来,当点位太多时影响浏览器的响应速度,很多图标叠加在一起也影响界面的美观性。
优化措施
根据以上分析,可以从以下几个方面进行优化。
视野范围内加载
-
思路
可以考虑只加载在视野范围内的安装点,以减少浏览器渲染的压力。
-
分析
将当前所有点位信息储存在一个数组中。当需要批量显示点位信息时,找出数组中在当前视野范围内的点并进行展示。
- 实现
以下是用到的PGIS相关接口:
- 查询当前地图显示范围:var boundsMBR =_MapApp.getBoundsLatLng()。
- 查询指定点是否在指定显示范围内:boundsMBR.containsPoint(Point)
- 隐藏一个图层。 PGIS没有提供隐藏一个图层的接口,但一个图层根本上来说是一个div,可以通过隐藏div来实现:pOverLay.div.style.display='none';
- 删除一个图层: _MapApp.removeOverlay(pOverLay)
具体的代码:
//隐藏当前视窗范围外的图层 function hidePointsOutOfBounds(){ var boundsMBR=_MapApp.getBoundsLatLng(); var zoomlevel=_MapApp.getZoomLevel(); for(var i=0;i<layerarray.length;i++){ pOverLay = layerarray[i]; if(boundsMBR.containsPoint(pOverLay.getPoint())){ if(layerflag &&((i+1)%zoomlevel!=0)){ _MapApp.removeOverlay(pOverLay); pOverLay.div.style.display='none'; }else{ if(pOverLay.div.style.display=='none'){ pOverLay.div.style.display=''; _MapApp.addOverlay(pOverLay); } } }else{ if(pOverLay.div.style.display==''){ _MapApp.removeOverlay(pOverLay); pOverLay.div.style.display='none'; } } } } |
分层加载
-
思路
如果一次将所有点位显示出来,相邻的点位会紧挨在一起,整个地图看起来元素很多,也不方便用户进行操作。可以考虑分层加载方法减少同时加载的点位数量。
-
分析
给每个点位设定层级范围,地图显示时获得当前比例尺层级,只显示当前层级下指定的点位。
-
实现
新增安装点层级表。表结构如下:
MONITOR_POINT_LAYR
名称 |
代码 |
数据类型 |
注释 |
默认值 |
||
安装点编号 |
POINTCODE |
NVARCHAR2(16) |
||||
最大层级 |
MAXLAYER |
NVARCHAR2(2) |
||||
最小层级 |
MINLAYER |
NVARCHAR2(2) |
在前台显示数据时,首先获得当前层级:
var zoomlevel=_MapApp.getZoomLevel(); |
然后循环安装点列表,获得每个安装点的最大层级和最小层级,判断是否满足条件:
minlevel<=Zoomlevel<=maxlayer |
只有满足该条件的安装点才进行显示。
不加载标题
- 思路
安装点标题现在默认显示在点位信息上面,可以考虑默认不显示标题,当鼠标移动到点上时再加载。
-
分析
每个安装点标题都占据一个单独的div层级,点位多时标题会挤在一起,影响美观性,也不方便查看。
-
实现
每个安装点在加载时默认隐藏标题:
marker.hideTitle();
添加鼠标监听事件,当鼠标移动上时显示标题,移出时隐藏标题:
marker.addListener("mouseover",function(){marker.showTitle()}); marker.addListener("mouseout",function(){marker.hideTitle()}); |
坐标点存储
-
思路
目前显示安装点需要同时查询业务库和PGIS空间数据库。业务库获得安装点明细信息,空间库获得经纬度坐标。单纯的数据展示可以考虑只查询一个数据库。设计到空间搜索等问题时再查询PGIS空间库。
-
分析
将安装点的坐标存到业务库中,这样在批量显示安装点时候不需要查询PGIS空间数据库就可以了。只有涉及到空间查询如点周边查询等操作时才操作PGIS空间数据库。
另外,可以在本地业务库中建立一张安装点空间表,将空间数据储存在自己的业务库中,这样空间查询等功能就不需要依赖PGIS提供的接口了,空间字段是Oracle提供的标准格式,更便于以后功能的扩展。
-
实现
平台monitor_point表已经有经纬度字段来,添加安装点时将经纬度数据填进去即可。具体代码略。
本地安装点空间表结构如下:
MONITOR_POINT_GEOMETRY
名称 |
代码 |
数据类型 |
注释 |
默认值 |
||
安装点编号 |
POINTCODE |
NVARCHAR2(16) |
||||
空间字段 |
GEOMETRY |
MDSYS.SDO_GEOMETRY |
数据缓存
-
思路
平台现在是实时查询数据库获得安装点信息,并展示在地图上。可以考虑将安装点信息缓存起来,调用的时候直接从缓存里读取数据,可以省掉查询数据库的操作。
-
分析
数据缓存可以在浏览器客户端缓存和服务器端缓存2种方式。
浏览器端缓存速度最快,但需要耗费浏览器端的内存,一条安装点的所有信息约占900个字节,经过实际测试,在客户端内存中缓存2179条数据,约占1.8M内存,并不影响浏览器响应速度。但当缓存约8000个点左右时会内存溢出。
因此可以考虑采用浏览器端和服务器端双重缓存的方案。只将基础数据(坐标点、安装点编号、名称、类型)缓存在浏览器客户端,将明细信息缓存到服务器端。
-
实现
首先查询数据库将安装点基础信息缓存到浏览器客户端,存在js数组里。当显示某类安装点时循环该数组将符合条件的安装点显示出来。
在服务器端查询数据库将明细信息缓存到一个cache文件中。该文件定时刷新以保证数据的及时性。当对安装点表进行操作时也实时将数据更新到cache文件中去。
点位聚合
参考点位聚合方案
专题图叠加 ※
-
思路
以上优化办法主要针对平台现有的地图显示需求,如果用户有大屏幕显示等特殊需求需要同时显示上千个点位,可以考虑专题图叠加功能。
-
分析
PGIS提供专题图叠加的接口,可以通过WMS协议调用第三方地图图片(如GeoServer)叠加到地图上。利用GeoServer等地图服务预先将安装点图层生成出来,利用PGIS的专题图叠加接口调用该图层。
- 实现
- 首先利用GeoServer生成一个安装点图层。
- 利用PGIS的接口调用该图层进行展示:
var legendfunc=new LegendFunc(); legendfunc.format=geoserverURL+"/wms?service=WMS&version=1.1.0&request= GetMap&layers="+layers+"&bbox=EZBOX&width=EZWIDTH &height=EZHEIGHT&srs=EPSG:4326&format=image/png &TRANSPARENT=true&cql_filter="+escape(cql_filter)+" &styles="+styles; legendfunc.open(_MapApp); |