如今的 GIS 应用离不开互联网,即 “WebGIS”,GIS 一个显著的特点就是数据量大,数据异构,因此在网络中传输数据成为了影响 WebGIS 应用性能的瓶颈。在 Web 浏览器的应用中尤为明显,浏览器下载数据时,等待时间过长,会造成浏览器假死现象。如何减小数据量,用小的数据量做出类似的大数据量效果是一个值得研究的问题,纵观 WebGIS 的发展史,不难得出结论:
Web地图服务的发展中一直伴随着缩小网络传输数据体积技术的发展
Web 地图服务的发展
地理信息领域,一般把现实世界的事物抽象成点、线、面三种几何形状,这些都是坐标或者坐标串形式存储的,有的存储在数据库中,有的存储在文件中(如 ESRI 的 Shapefile 格式),数据精度越高,数据量就越大。当我们要通过网络访问这些数据时,如果直接返回,在客户端渲染成图,那么有两个弊端:
- 传输数据量大,浪费带宽,延迟时间长;
- 客户端硬件配置不一,计算能力有限,无法保证体验一致。
这时候,人们想到一个办法:把空间坐标数据在服务器端渲染,以图片形式返回,且图片的体积比原始坐标数据小,可以克服了以上两个弊端。但是,一般情况下,用户只需要查看某个局部区域的某个缩放级别下的数据,如果每次都返回整个世界的地图图片,还是浪费带宽,聪明的 GISer 们又将整张地图分为很多同样大小的切片,对它们编号,按规律存储。在客户端获得用户需要的地图范围和缩放级别,只返回相应范围和缩放级别的切片。
这种方式还是有一定的缺陷,缺乏灵活性,如今的城市和农村都在发展,新道路、新建筑一天一个样,而利用切片地图的技术,新的数据不能实时反映到地图中,因为数据是在服务器预先处理好的。
GIS 数据分类
GIS 数据可以分为两类:矢量和栅格,矢量数据比较灵活,实时性强,可以在客户端渲染,但是数据体积大(因为是原始坐标数据);栅格数据相较于同范围的矢量数据,体积较小,但缺乏灵活性和实时性。一般情况下,因为现实世界变动较小,不频繁,所以用栅格数据作为底图,用矢量数据表示变动频繁的地物或者现象,或者按需请求的情况。
如何解决需要传输数据体积小并且灵活,实时性好呢?
两个思路
利用矢量数据
在客户端获取地理范围和缩放级别,只返回相应范围和缩放级别的矢量数据,在客户端渲染矢量数据,这样既减少了传输体积,也发挥了矢量数据的灵活性和实时性的优势,这就是 “矢量切片” 技术,这里不详细展开,我会在其他文章中介绍。
简化传输数据
以绘制车辆轨迹为例,一般传回数据库的 GPS 位置数据比较密集,数据冗余比较大,但是用户查看轨迹一般不要求精确,只需要看到大致轮廓即可,因此,可以尝试简化轨迹数据,这里用到的算法就是“拉默-道格拉斯-普克”算法,它可以有效减少组成线的点个数,使折线的数据量显著减少,但仍保持轨迹的大致骨架。这里主要介绍这个算法。
拉默-道格拉斯-普克 算法
算法思路
该算法的思想是设置一个距离阈值,当点到线的距离小于该阈值,那么点视为可以删除的点。最后剩下的点即为最终结果。
给定数据:
- 按线数据排序好的坐标数组;
- 距离阈值 epsilon
具体步骤如下:
- 将首尾点标记为保留点,首尾点连线记为线 L1;
- 在首尾点之间的剩余点中找到距离 L1 最远的点 P,如果点 P 到直线的距离 d 大于阈值 epsilon,那么将点 P 标记为保留点,否则点 P 为待剔除点;
- 将首点和点 P 连接构成线 L2;将尾点和点 P 连接构成线 L3,分别重复步骤 2;
- 一直到点小于三个点时停止。
PHP 实现
// 拉默-道格拉斯-普克 算法实现
// @param {array} $lineArr 坐标组成的数组
// @param {float} $epsilon 点到直线距离阈值
public function RDP ($lineArr, $epsilon)
{
// global $keptPointArr;
// 线段长度必须大于3
$pointsNum = count($lineArr);
if ($pointsNum < 3) {
return;
}
// 递归调用
$dmax = 0;
$index = 0;
for ($i = 1; $i<($pointsNum-1); $i++) {
$startPoint = $lineArr[0];
$endPoint = $lineArr[$pointsNum-1];
$distance = $this->linePointDistance([$startPoint, $endPoint], $lineArr[$i]);
if ($dmax < $distance) {
$dmax = $distance;
$index = $i;
}
}
if ($dmax > $epsilon) {
array_push($this->keptPointArr, $lineArr[$index]);
$this->RDP(array_slice($lineArr, 0, $index+1), $epsilon);
$this->RDP(array_slice($lineArr, $index, $pointsNum-$index), $epsilon);
} else {
array_push($this->keptPointArr, $lineArr[0]);
array_push($this->keptPointArr, $lineArr[$pointsNum-1]);
return;
}
}
其中,linePointDistance()
函数是计算点到线的距离,返回最大距离,接受三个参数,第一个参数是线的首尾点组成的数组,第二个参数是点;keptPointArr
是保留点组成的数组。该算法使用的递归的思想。
完整的代码可以到我的 GitHub 看:https://github.com/QingyaFan/common-gis-algorithm/blob/master/ramer-douglas-peucker.php。
总结
简要总结了服务器端地图服务器的发展,面对当今社会瞬息万变的世界,我们的 GIS 的技术,也在不断发展,来满足当今社会对灵活性、实时性、大数据、智能化的需求。本文介绍的 “拉默-道格拉斯-普克” 算法是很早就出现的算法,应用在现在数据爆炸的时代反而比较合适。
面对当今社会对于实时性的要求,矢量切片
技术应运而生,在之后的文章中,我会介绍 矢量切片
技术的原理和技术。