路况和轨迹播放综合效果
maptalks体系内不支持对一条线进行分段着色的,如果你需要做这种路况效果需要你自己拆分线路,将其拆成一段段的,且携带自己的 业务信息
总的思想就是:路况数据用一个独立的图层来加载,然后把这些分段数据合并成一个整条路线作为路线播放的数据源
轨迹播放插件
maptalks.routeplayer是个轨迹播放插件,该示例里我们使用它, 其具体的API等请参考其官方仓库里的说明
其事件有:
- playstart
- playfinish
- playing
- playpause
js
player.on('playing',e=>{
});
关于轨迹的数据结构
json
{
"path": [
[
116.379028,
39.865042,
0,
1694591213642
],
[
116.379742,
39.865566,
0,
1694591517503.7537
],
...
]
}
即 [经度,纬度,海拔,时间戳,其他等]
WARNING
这个很重要请要注意,自己的业务如果数据结构不同,需要自己转成改数据结构
准备数据
这里我偷懒了,直接使用了高德的路线规划的数据来进行测试,所以在例子你会看到一些数据转换的代码的,比如坐标转换等,主要是 我也没有测试数据,所以只能找些测试数据,因为数据结构的不同故而导致代码会掺杂这这些无关的代码
json
{
"status": "1",
"info": "OK",
"infocode": "10000",
"count": "3",
"route": {
"origin": "116.379028,39.865042",
"destination": "116.69569267333372,40.002403206049195",
"taxi_cost": "125",
"paths": [
{
"distance": "45271",
"duration": "4677",
"strategy": "速度最快",
"tolls": "11",
"toll_distance": "6797",
"steps": [
{
"instruction": "向东南行驶108米左转",
"orientation": "东南",
"distance": "108",
"tolls": "0",
"toll_distance": "0",
"toll_road": [],
"duration": "92",
"polyline": "116.379742,39.865566;116.379943,39.865404;116.380056,39.86535;116.380222,39.865173;116.380335,39.865028;116.380475,39.864787",
"action": "左转",
"assistant_action": [],
"tmcs": [
{
"lcode": [],
"distance": "36",
"status": "拥堵",
"polyline": "116.379742,39.865566;116.379943,39.865404;116.380056,39.86535"
},
{
"lcode": [],
"distance": "72",
"status": "拥堵",
"polyline": "116.380056,39.86535;116.380222,39.865173;116.380335,39.865028;116.380475,39.864787"
}
],
"cities": [
{
"name": "北京城区",
"citycode": "010",
"adcode": "110100",
"districts": [
{
"name": "丰台区",
"adcode": "110106"
}
]
}
]
},
{
"instruction": "向东行驶58米靠右",
"orientation": "东",
"distance": "58",
"tolls": "0",
"toll_distance": "0",
"toll_road": [],
"duration": "20",
"polyline": "116.380475,39.864787;116.380576,39.864739;116.380684,39.864728;116.381129,39.864798",
"action": "靠右",
"assistant_action": [],
"tmcs": [
{
"lcode": [],
"distance": "58",
"status": "严重拥堵",
"polyline": "116.380475,39.864787;116.380576,39.864739;116.380684,39.864728;116.381129,39.864798"
}
],
"cities": [
{
"name": "北京城区",
"citycode": "010",
"adcode": "110100",
"districts": [
{
"name": "丰台区",
"adcode": "110106"
}
]
}
]
},
...
],
"restriction": "0",
"traffic_lights": "7"
}
]
}
}
数据结构转换的一些工具函数
TIP
这些代码在你的业务不一定需要,这里只是因为测试数据结构的差异导致的额外的工作而已
js
//简单的经纬度数据处理
function formatLngLats(str) {
if (str.indexOf(';') === -1) {
const lnglat = str.split(',').map(v => {
return parseFloat(v);
});
lnglat.push(0);
return lnglat;
}
const array = str.split(';');
return array.map(c => {
return formatLngLats(c);
})
}
function getColor(status) {
if (status === '畅通') return;
if (status === '缓行') return '#FFA700';
if (status === '拥堵') return '#E94B37';
}
//整条路线的坐标数据
function getWholeCoordinates(route) {
let str = '';
const { origin, destination, paths } = route;
str += origin;
const path = paths[0];
const steps = path.steps;
steps.forEach(step => {
const { polyline } = step;
str += ';';
str += polyline;
});
str += ';';
str += destination;
return formatLngLats(str);
}
加载路况路线
代码层面很简单,就是把分段的线路都构造一个LineString
即可,根据业务的信息为每条线配置不同的颜色
js
//添加基本的路线
function addBaseLine(route) {
const line = new maptalks.LineString(getWholeCoordinates(route), {
symbol: {
lineColor: 'green',
lineWidth
}
}).addTo(layer);
}
//分段路线数据
function addLines(path) {
const steps = path.steps;
steps.forEach(step => {
const tmcs = step.tmcs || [];
tmcs.forEach(tmc => {
const { polyline, status } = tmc;
const lineColor = getColor(status);
if (!lineColor) {
return;
}
const coordinates = formatLngLats(polyline);
const line = new maptalks.LineString(coordinates, {
symbol: {
lineColor,
lineWidth
}
});
line.addTo(layer);
});
});
}
//起始点
function addPoints(route) {
const { origin, destination } = route;
const point = new maptalks.Marker(formatLngLats(origin), {
symbol: {
markerFile: './../assets/image/start.png'
}
}).addTo(layer);
const point1 = new maptalks.Marker(formatLngLats(destination), {
symbol: {
markerFile: './../assets/image/end.png'
}
}).addTo(layer);
}
轨迹播放
- 把所有的分段路线合并成一个整条的路线
- 为每个坐标点赋值时间戳,这个只是因为是测试数据里没有时间戳导致的,手动计算了下这个值,这个值应该在你的业务数据携带
- 默认关闭轨迹播放里的路线显示选项,即轨迹播放的线条是隐藏在背后
js
//轨迹播放
function addRouterLine(route) {
const coordinates = getWholeCoordinates(route).map(c => {
return new maptalks.Coordinate(c);
})
const coords = [];
//相邻点重复的去重,如果你的业务不需要可以省去改步骤
for (let i = 0, len = coordinates.length; i < len; i++) {
const c = coordinates[i];
const lastC = coords[coords.length - 1];
if (!lastC || !c.equals(lastC)) {
coords.push(c);
}
}
//给坐标点赋值时间戳,这个值正常应该坐标数据里就携带,这里只是模拟下的
let time = new Date().getTime();
const path = [];
const speed = 1 * 1000 / 3600;
for (let i = 0, len = coords.length; i < len; i++) {
if (i === 0) {
const lnglat = coords[i].toArray();
lnglat.push(time);
path.push(lnglat);
} else {
const c2 = coords[i], c1 = coords[i - 1];
//两点之间的距离
const distance = map.computeLength(c2, c1);
//时间差
const offsetTime = distance / speed * 1000;
time += offsetTime;
const lnglat = c2.toArray();
lnglat.push(time);
path.push(lnglat);
}
}
player = new maptalks.RoutePlayer({ path }, map, {
maxTrailLine: 100,
showTrail: false,
//不显示线路
showRoutes: false,
// showMarker: false,
lineSymbol: {
lineColor: '#f0f',
lineWidth: lineWidth + 2
},
markerSymbol: {
markerFile: './../assets/image/car.svg',
markerWidth: 40,
markerHeight: 40
}
});
unitTime = player.getUnitTime();
}
function loadData() {
fetch('./../assets/data/tripline.json').then(res => res.json()).then(json => {
const route = json.route;
const path = route.paths[0];
addBaseLine(route);
addLines(path);
addPoints(route);
addRouterLine(route);
})
}
loadData();
TIP
maptalks.routeplayer 也支持3D图层的,具体的请参考其仓库