前言
在一些共享出行应用的地图中,以美团共享骑行为例,城市的重要节点会设置电子围栏,防止单车无序停放造成交通障碍。比如在星城,湘江一桥及橘子洲景区就划定了禁停区——打开地图就能看到:

这里的禁停区,就是电子围栏的典型应用。同样,在GIS安防领域,这类技术也层出不穷:在地图上画一个围栏,配合安装定位的自行车,就能实现基于位置的管理。
还有一类需求更常见:获取车辆、行人或工程机械的GPS轨迹后,不仅需要画出轨迹线,还希望能大致还原目标的活动范围。这个范围可能是矩形框(GIS里的bbox概念),也可能是包含所有轨迹点的最大包围框——一个面数据,而非矩形。那么,这种需求该怎么处理?前端能实现吗?
这篇文章将重点介绍实现方式,主要讲解Turf.js在求解范围多边形时的两种方法,配合代码让读者快速掌握,并能在实际项目中直接落地。如果你正在WebGIS中做类似展示,不妨往下看。
一、场景需求
先聊聊项目里真实遇到的一个场景,权当抛砖引玉——场景各不相同,如果恰好相似,那也算缘分。需求是这样的:在WebGIS中展示某台安装了定位的工程机械每日的工作点位移动情况,以天为单位,把所有点位展示在地图上。在此基础上,还需要求解该机械的最大活动范围,并用不同颜色标识出来。
刚开始拿到这个需求时,我们想得很简单:把所有点整合起来,用Leaflet的getBounds()方法获取包围框。但getBounds()返回的其实是一个最大矩形——覆盖了当前空间数据四至的矩形。当时没仔细考虑,结果矩形边界把大量空白区域也包了进去,因为矩形四条边是直线,很多实际并未到达的区域也被划入范围。
所以,需要一种更灵活的方式,更精准地标记范围——既要包含所有目标点,又不浪费多余空间。
1、Leaflet.js的不足
之前的博客里介绍过Leaflet,它是二维地图开发的优秀组件,可以直接用于WebGIS系统开发,作为底图展示和可视化的基座。但面对那些需要动态绘制的空间对象,Leaflet的能力稍显欠缺。翻了半天资料,没找到太便捷的实现方式,于是考虑借助其他技术组件来满足需求。
2、Turf.js
Turf.js是一款面向WebGIS的前端地理空间分析库,内含许多实用小工具,项目开发里可以无缝衔接。因为是客户端产品,数据安全性大大提高——无需将数据发送到远端,前端就能对后端返回的数据进行按需组合和可视化分析。关于Turf的好处不多说,感兴趣的朋友可以去它的中文网或官网看看。
选择Turf.js的原因很简单:分析功能强大,能自动计算空间数据的bbox和最小外包多边形(支持凸多边形和凹多边形)。下面通过实例,分别说明bbox和外包多边形的生成方法。
二、原始数据展示
先看看原始数据在WebGIS中的展示效果。工程机械的轨迹数据如下,通过接口可以获取GPS位置信息。示例数据如下:
"lat": 28.0394672,"lng": 112.9439207,"cog": 0.0,"p_distance": 49.0,"img_url": "https://agri-static.holdingbyte.com/d-1000-awwwhqgwyxqe/3ecced35-e23f-4e91-ae34-4bef6294f4c0.jpg","t": 1693124829,"agri_id": "d-1000-awwwhqgwyxqe-50-00","the_type": 301,"__db_name__": "0","t_display": "2023-08-27 08:27"
| 序号 | 参数 | 说明 |
|---|---|---|
| 1 | lng | 经度,112.9439 |
| 2 | lat | 纬度,28.039 |
| 3 | t | 时间 |
| 4 | 其它参数忽略 | 其它参数不重要,暂时忽略 |
实际情况下,这些是连续的点位数据,而非孤立点。上面截取了一段示例数据供描述参考,完整数据体大致如下:

1、点位数据展示
为了在空间上展示这些点位,WebGIS可视化组件还是选熟悉的Leaflet。简单回顾一下在Leaflet中展示点位信息的方法。
创建点位框架
新建index2.html页面,输入以下内容:
turf生成边界凸多边形实战
定义地图
在上面的HTML中增加Ja vaScript脚本,定义并初始化地图,关键代码如下:
//声明图层组var workPoints = L.layerGroup();var wjLineGroup = L.layerGroup();var baseLayer = L.tileLayer('./yxtile/{z}/{x}/{y}.png', {minZoom: 14,maxZoom: 21,tms: false,attribution: 'turf生成边界凸多边形实战'});//声明地图并添加图层var map = L.map('map', {center: [28.038387, 112.951566],zoom: 16,layers: [baseLayer,wjLineGroup]}); //新建图层控件的数据源-地图var baseLayers = {'离线底图': baseLayer};//新建图层控件的数据源-城市var overlays = {'行驶轨迹': wjLineGroup};
2、定义样式
为了在程序中复用样式,将样式生成函数封装,支持传递颜色进行自定义设置。使用的时候传入想要的颜色即可(温馨提示:这里只实现了颜色参数的自定义,其他参数可模仿此方法扩展)。
function getStyle(color){return { color: color, weight: 3, opacity: 1, fillColor: color, fillOpacity: 0.3, fill: true, stroke: true }}
3、定位数据初始化
定位数据通过后台接口提供,这里为了演示方便,将后台接口数据做了静态临时存储,不影响最终效果。关键代码如下:
var lineArray = new Array();var turfArray = new Array();$(document).ready(function(){$.ajax({url: "data.json", //模拟从服务器获取JSON数据的URL地址(URL必须与服务器上实际的文件名相同)type: "GET",dataType: "json",success: function(data) {var resData = data;for(var i=0;i
在浏览器中运行以上代码,可以看到轨迹数据在Web上的展示效果:

三、Turfjs中bbox生成
上面的例子成功展示了轨迹数据。但怎么得到它的边界范围呢?下面深入讲解。
1、官网讲解
先看turfjs中文网对bbox生成的介绍。Takes a set of features, calculates the bbox of all input features, and returns a bounding box. 获取一组feature,计算所有feature的bbox,并返回一个边界框。
参数说明:
| 参数 | 类型 | 描述 |
|---|---|---|
| geojson | GeoJSON | 任何 GeoJSON 对象 |
返回值:BBox - bbox在minX, minY, maxX, maxY顺序中的扩展
来看官方示例:
var line = turf.lineString([[104.99467, 30.071677],[107.13797, 36.550462],[112.607082, 34.991467]]);var bbox = turf.bbox(line);var bboxPolygon = turf.bboxPolygon(bbox);

2、轨迹bbox生成
首先,定义一个数组接收数据:var turfArray = new Array(); 在for循环中添加新点。
for(var i=0;i
var points = turf.featureCollection(turfArray);var range = turf.bboxPolygon(turf.bbox(points));//turf生成最小外包框、bboxL.geoJSON(range,{style:getStyle("red")}).addTo(map);

可以看到,bbox生成的矩形范围框包含了许多未到达的区域。下面再来生成最小外包多边形,看看效果如何。
四、Turfjs生成外包多边形
1、官网例子
Takes a Feature or a FeatureCollection and returns a convex hull Polygon. 接受一个Feature或FeatureCollection,并返回凸多边形。
参数说明:
| 参数 | 类型 | 描述 |
|---|---|---|
| geojson | GeoJSON | Feature 或 FeatureCollection |
| options | Object | 可选参数:见下文 |
options选项:
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| conca vity | number | Infinity | 1 - thin shape. Infinity - 凸多边形 |
var points = turf.featureCollection([turf.point([10.195312, 43.755225]),turf.point([10.404052, 43.8424511]),turf.point([10.579833, 43.659924]),turf.point([10.360107, 43.516688]),turf.point([10.14038, 43.588348]),turf.point([10.195312, 43.755225])]);var hull = turf.convex(points);

2、凸多边形生成
var hull = turf.convex(points);//turf 生成凸多边形最小外包框L.geoJSON(hull,{style:getStyle("yellow")}).addTo(map);

注意,这里的最小外包多边形(黄色多边形区域)基本满足要求——范围最小、覆盖最精准。
总结
从实际效果看,Turf.js的凸多边形比简单的矩形bbox更贴合真实的活动范围。如果你也遇到类似需求——需要在前端根据GPS轨迹生成更精准的包围多边形,不妨试试这两种方法。bbox简单粗暴,适合快速预览;凸多边形则更紧凑,适合正式展示。当然,如果轨迹非常不规则,还可以调整conca vity参数生成凹多边形,进一步贴近真实边界。希望这篇实战经验对你有帮助。
