【Openlayers】纯前端 实现OL叠加 实时交通路况图
程序员文章站
2022-07-14 21:39:04
...
1.说明:
- 声明:有关叠加路况图这个功能,我找了好久都没有找到前端怎么做。故这个文章应该是全网第一篇纯前端实现的。有很多不足的地方,欢迎大家指出。
- 在写完交通【Openlayers】完全前端实现 驾车路径规划这个文章的时候,我就想通过这个路径规划这个API,我可以写一个交通实时路况的叠加。因为拿到每条规划路线的数据里面有路况的信息。我只需要在屏幕上合理的取点,然后组成要搜索的起点终点、途经点的数据处理,就可以实现我们的需求。但是.....根本没有我们想象的那么简单。但是总体思路是正确的。
- 在其他人写的博客上也找到有交通路况的叠加,别人的实现,我大概看了下,他的实现思路把路况的数据在后台进行切片处理,对,这个确实是个最好的方式,也是最省浏览器的方式。但是我是前端,我只能从前端的角度去考虑这个问题。所有下面的代码全部是纯前端实现,有真实需要该业务的可以绕行,目前纯前端效果一般,性能一般。
2.效果:
3.代码:
3.1 初期想法:
- 初期想法比较简单,就是我拿到屏幕的四个点的坐标,根据自己设定的lng向和lat方向的分割参数,计算出屏幕上所有的点的坐标,然后根据没点的坐标,寻找右边和下边的点形成起始点、终止点和途经点的数据,注入路径规划API,然后进行数据渲染。上图:四个黑色箭头就是屏幕四个点,我现在设置lng方向分为4段,lat方向分为3分,然后就可以屏幕上我们需要的点,每个点都有自己的坐标和ID,ID是自己命名的。便于寻找其他数据。
-
用ID寻找数据:例如我们在1_1点上,我们要寻找的: 1_2作为起始点,2_1作为终止点,1_1作为途经点。为什么要这样找点,你想。你把规则的屏幕上的排列点,都是按照这样的方式找点和规划路线,是不是就是相当于把屏幕上所有点按照真实环境下最合理的方式会把所有的路的数据都请求到。
- 但是:出来的效果不怎么好,因为搜出来的路线数据,大部分不是我们想要的主路的数据,都是些小路或者辅路,未知路上的一些路况信息,不是我们想要的。
- 主要是一开始我们的这个思路有点不合理,屏幕上通过我们设置的份数进行屏幕上所有的点,但是无论我们怎么调试我们的份数的这个参数,算出来的点坐标都不是最合理的,原因就是这些点的坐标没有能尽量的落在主路上。所以上周就是这样的想法,直接放弃。代码重构。这个错误的思路促使找到正确的路的数据。
3.2 现在的想法:
- 接着上面的想法,我们需要找屏幕上所有的标识为路的数据,高德有API可以实现:
- 我们只需要 searchInBounds()这个API,其中这个Bounds就是我们屏幕的四个角点的坐标。
- 搜索的数据量设置为50个点。为了更准确搜寻到我们需要的标识为路的数据,我分别搜索了:国道、省道、县道、高速等关键字。搜索的函数封装为promise。
// 高德初始化
_gd: function(cb) {
me.gd_map = new AMap.Map("map_gd", {
zoom: me.conf.gd_map.zoom,
zooms: me.conf.gd_map.zooms,
});
me.gd_map.on('complete', function(e) {
// 搜索插件
me.gd_map.plugin(["AMap.PlaceSearch"], function() {
//构造路线导航类
me.all_obj.traffic.search_tool = new AMap.PlaceSearch({ //构造地点查询类
pageSize: 50,
pageIndex: 1,
map: me.gd_map,
children: 1,
type: '道路附属设施|交通设施服务|地名地址信息',
extensions: "all",
});
// cb && cb();
// 导航插件
me.gd_map.plugin(["AMap.Driving"], function() {
//构造路线导航类
me.all_obj.traffic.traffic_tool = new AMap.Driving({
map: me.gd_map,
});
cb && cb();
});
});
});
},
_search: function() {
// 清除画布
me.all_obj.traffic.data_c.clear();
me._search_view_data();
me.all_obj.traffic.load = layer.load(0, { shade: 0.5 });
me._search_init(function() {
// 数据处理
...
});
},
// 搜索初始化
_search_init: function(cb) {
me.all_obj.traffic.search_arr.length = 0;
me._search_start("国道")
.then(function(arr) {
me._search_gen_arr(arr);
return me._search_start("省道");
})
.then(function(arr) {
me._search_gen_arr(arr);
return me._search_start("县道");
})
.then(function(arr) {
me._search_gen_arr(arr);
return me._search_start("高速");
})
// .then(function(arr) {
// me._search_gen_arr(arr);
// return me._search_start("高速口");
// })
// .then(function(arr) {
// me._search_gen_arr(arr);
// return me._search_start("路口");
// })
.then(function(arr) {
me._search_gen_arr(arr);
// console.log(me.all_obj.traffic.search_arr);
cb && cb();
});
},
// 开始搜索
_search_start: function(key) {
return new Promise(function(resolve, reject) {
me.all_obj.traffic.search_tool
.searchInBounds(
//
key,
//
new AMap.Polygon({
path: me.all_obj.traffic.points_4,
}),
//
function(status, result) {
// console.log();
resolve(result.poiList.pois);
});
});
},
- 现在搜索到的数据点,标识为地图上如下:
- 接下来就是对数据进行分组处理,以16数据为一个数组进行分成多个小组。取值为16的原因是途经点最多可以设置为16个:
- 拿到搜索的数据后进行分组处理(够16个点为一组):
_search_data_handle: function() {
console.log(me.all_obj.traffic.search_arr);
// 标识
me.all_obj.traffic.search_arr.forEach(function(ele, index) {
me._marker(ele.location);
});
var p_arr = [];
var s_arr = [];
var index_limit = 0;
me.all_obj.traffic.search_arr.forEach(function(ele, index) {
//
if (index % 16 == 0) {
index_limit = index + 16;
// console.log(index_limit);
s_arr = [];
}
// 满16的数据
if (index_limit < me.all_obj.traffic.search_arr.length) {
s_arr.push(ele);
if (s_arr.length == 16) {
p_arr.push(s_arr);
}
}
// 不满16
else {
s_arr.push(ele);
if (index == me.all_obj.traffic.search_arr.length - 1) {
p_arr.push(s_arr);
}
// console.log(s_arr)
}
});
me.all_obj.traffic.search_arr_16.length = 0;
me.all_obj.traffic.search_arr_16 = p_arr;
console.log(me.all_obj.traffic.search_arr_16);
},
- 分为16个点为一组的数据就是我们要搜索的一条线的数据,为此我这样的数据模型:每组数据的起始点为上一组数据的末尾点、终止点为下一组数据的第一个点、途经点就是当前组的数据。
- 特别的:第一组的数据的起始点就是最后一组数据的末尾点。最后一组的末位点就是第一组数据的第一个点。用图表示就是:
// 一条线的数据生成
_search_one_line_data: function(line_arr, line_index) {
// console.log(line_arr, line_index);
var start_line = null,
end_line = null,
start_p = null,
end_p = null;
// 第一条线
if (line_index == 0) {
start_line = me.all_obj.traffic.search_arr_16[me.all_obj.traffic.search_arr_16.length - 1];
end_line = me.all_obj.traffic.search_arr_16[line_index + 1];
}
// 最后一根线
else if (line_index == (me.all_obj.traffic.search_arr_16.length - 1)) {
start_line = me.all_obj.traffic.search_arr_16[line_index - 1];
end_line = me.all_obj.traffic.search_arr_16[0];
}
// 中间线
else {
start_line = me.all_obj.traffic.search_arr_16[line_index - 1];
end_line = me.all_obj.traffic.search_arr_16[line_index + 1];
}
// console.log(start_line);
// console.log(end_line);
//
start_p = start_line[start_line.length - 1];
end_p = end_line[0];
// console.log(start_p);
// console.log(end_p);
//
var waypoints = [];
line_arr.forEach(function(p, index) {
waypoints.push(new AMap.LngLat(p.location.lng, p.location.lat));
});
return {
start_p: new AMap.LngLat(start_p.location.lng, start_p.location.lat),
end_p: new AMap.LngLat(end_p.location.lng, end_p.location.lat),
waypoints: waypoints,
};
},
-
然后就是递归式渲染搜索的路况数据:
// 拿到的数据进行渲染
_search_data_inj: function(index) {
// 拿到数据
var p_obj = me._search_one_line_data(me.all_obj.traffic.search_arr_16[index], index);
// console.log(p_obj);
// return;
// 渲染数据
me._search_one_line_init(p_obj, function() {
index++;
if (index == me.all_obj.traffic.search_arr_16.length) {
layer.close(me.all_obj.traffic.load);
return;
}
me._search_data_inj(index);
});
},
- 最后就是配合地图移动事件进行屏幕区域的路况叠加:
_ol_map_ev: function() {
me.ol_map.getView()
.on('change:center', function() {
me._search();
});
},
_search: function() {
// 清除画布
me.all_obj.traffic.data_c.clear();
me._search_view_data();
// me.all_obj.traffic.load = layer.load(0, { shade: 0.5 });
me._search_init(function() {
// 数据处理
me._search_data_handle();
// 渲染
me._search_data_inj(0);
});
},
4.问题
- 其实现在这个思路比最一开始的思路强很多了,但是问题还是很多。
- 问题1:对搜索到的数据由于组合数据不合理,形成的每条分组数据进行搜索路径状态的数据,会有很多重复的地方。反应在地图上就是一个路段可能会被重复覆盖多次。这个问题的想的解决方式:就是有个优化数据的模型,可以取到所有的点在地图上最外层的数据一层一层往里面进行过滤,最终就是把比较挨着比较近的数据进行舍弃,提高屏幕所搜索点的数据的均匀分布和最少数据量。这个只是我的设想,有更好数据优化的小伙伴可以留言一起讨论学习。这个问题解决了,直接可以提高这个功能的性能。
- 问题2:代码优化,每次对搜索到的数据进行分段渲染的时候,函数内部的变量会多次累计,直接影响浏览器的性能,这个设置一个全局变量就可以解决。本版本没有进行优化。