在GIS路线相关的应用开发中,展示轨迹是一个常见的需求。通常,我们需要在地图上采集若干个关键点的经纬度,然后将它们连接成一条路径。但有一个问题令人困扰:如果只采集少量关键点,最终生成的折线会显得十分生硬,尤其在拐点处非常突兀。简单来说,效果如下:

那么,有没有办法让这些拐点连接而成的曲线变得更加平滑自然呢?有人可能会说:多采集一些点不就行了?理论上确实如此,点越多曲线越平滑。然而,实际野外数据采集工作并不轻松,点位越多意味着工作量越大。那么,能否在不增加采集工作量的前提下,基于现有数据直接进行平滑处理呢?
本文分享一种基于Turf.js的WebGIS解决方案。该方案可以在不增加点位采集负担的情况下,动态生成平滑曲线并在地图上展示。对于希望在WebGIS中实现类似功能的技术人员而言,这是一种值得掌握的实用方法。
一、问题的由来
线是由点构成的。将点两两相连,就能形成一条线。为了便于演示,我们模拟了一条虚拟的自驾路线,并使用Turf.js生成不同平滑程度的曲线。
先回到最基础的场景:以Leaflet作为地图组件,搭配Turf.js,查看初始效果。
1、创建网页框架
首先创建一个HTML页面,页面中需要引入Leaflet的CSS和JS文件,以及Turf.js的库。核心代码如下:
基于Leaflet和Turf生成平滑曲线实践
2、创建map对象
基础框架搭建完成后,定义地图对象并将容器绑定上去:
var mymap = L.map('map').setView([29.052934, 104.0625], 6);
var tileLayer = L.tileLayer('https://localhost:8086/data/xxgc/q0403/{z}/{x}/{y}.png', {
maxZoom: 7,
minZoom:0
});
tileLayer.addTo(mymap);
3、构建点位,生成路线
为了演示,我们使用一套虚拟数据,模拟一条从云南保山出发,途经攀枝花、昆明、成都、重庆、贵阳、长沙、赣州,最终到达福建福州的自驾路线。各城市的经纬度如下:
var cityPoint = new Array();
cityPoint.push([99.116482, 25.078402]);//保山
cityPoint.push([101.708392, 26.50289]);//攀枝花
cityPoint.push([102.780718, 24.915559]);//昆明
cityPoint.push([104.098757, 30.594412]);//成都
cityPoint.push([106.559098, 29.452047]);//重庆
cityPoint.push([106.515163, 26.461228]);//贵阳
cityPoint.push([112.929622, 28.141659]);//长沙
cityPoint.push([114.956049, 25.713705]);//赣州
cityPoint.push([119.344081, 26.027307]);//福州
然后将这些点位连接成线,并展示在地图上:
var linestring = turf.lineString(cityPoint, {name: 'line 1'});
L.geoJSON(linestring,{
style:{color:"blue",wight:2,fill:false}
}).addTo(mymap);
查看原始效果,注意各个拐点——确实不够平滑。

二、Turf.js平滑曲线改造
现在进入核心环节。先简单了解Turf.js中用于曲线平滑的方法。
1、官网方法介绍
Turf.js提供了bezierSpline()方法,用于对多线段进行平滑处理。其原理是通过贝塞尔样条算法,将原始线转换为弯曲的版本。关于贝塞尔曲线的具体原理,感兴趣的朋友可以自行查阅,其中涉及不少数学知识。
方法参数如下:
| 参数 | 类型 | 描述 |
|---|---|---|
| line | Feature |
输入线要素 |
| options | Object | 可选参数:见下文 |
options选项:
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| resolution | number | 10000 | 点之间的时间间隔(毫秒) |
| sharpness | number | 0.85 | 衡量样条路径弯曲程度的度量 |
返回值是Feature
2、0.4弯曲度曲线
在实际应用中,我们将sharpness设置为0.4,观察效果:
var curved = turf.bezierSpline(linestring,{sharpness:0.4,resolution:10000});
L.geoJSON(curved,{
style:{color:"red",wight:2,fill:false}
}).addTo(mymap);
将生成的红色曲线与原始蓝色曲线叠加在一起,可以明显看出整条路线平滑了许多。

3、0.85弯曲度曲线
接下来尝试0.85的弯曲度:
L.geoJSON(turf.bezierSpline(linestring,{sharpness:0.85,resolution:10000}),{
style:{color:"green",wight:2,fill:false}
}).addTo(mymap);

从效果来看,0.85的曲线弯曲程度明显更大,与真实路线的形态差异也更为突出。
4、0.1弯曲度曲线
再试试0.1的弯曲度:
L.geoJSON(turf.bezierSpline(linestring,{sharpness:0.1,resolution:10000}),{
style:{color:"yellow",wight:2,fill:false}
}).addTo(mymap);

5、综合对比
将三条不同弯曲度的曲线叠加在一起进行对比,效果更加直观:

从对比中可以看出:弯曲度系数越大,曲线越“弯”。黄色代表0.1弯曲度,红色代表0.4弯曲度,绿色代表0.85弯曲度。
| 序号 | 弯曲度 | 实际效果 |
|---|---|---|
| 1 | 0.1 | 与原始折线贴合度高 |
| 2 | 0.4 | 产生一定弯曲 |
| 3 | 0.85 | 弯曲幅度较大 |
最后附上完整的示例代码。运行时请将图源地址替换为自己服务器上的地址。
基于Leaflet和Turf生成平滑曲线实践
总结
简而言之,利用Turf.js的bezierSpline方法配合sharpness参数,可以在不增加采集点数量的情况下,高效地对已有路线数据进行平滑处理。这种方法既不会破坏原始路径的走向意图,又能有效优化拐点效果。在WebGIS轨迹平滑、路线可视化等场景中,这是一种优雅且实用的解决方案。
