跳到主要内容
版本:9.0.0

车辆轨迹线

show
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<title>车辆轨迹线</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src='https://delivery.mapmost.com/cdn/sdk/webgl/v9.0.0/mapmost-webgl-min.js'></script>
<style>
body {
margin: 0;
padding: 0;
}

#menuBar {
position: relative;
top: 10px;
left: 10px;
padding: 5px;
border-radius: 3px;
z-index: 999;
}

button {
font-size: 16px;
}

#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>

<body>
<div id="map"></div>
<div id="menuBar">
<button onclick="startClick()">开始</button>
<button onclick="pauseClick()">暂停</button>
<button onclick="stopClick()">停止</button>
</div>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
<script src="https://delivery.mapmost.com/cdn/sdk/plugins/mapmost-webgl-draw/v1.0.0/mapmost-webgl-draw.js"></script>
<link rel="stylesheet" href="https://delivery.mapmost.com/cdn/sdk/plugins/mapmost-webgl-draw/v1.0.0/mapmost-webgl-draw.css" type="text/css">
<script>
var map = new mapmost.Map({
container: 'map',
style: '<your style url>',
center: [120.73934512382101, 31.325101259633257],
zoom: 13,
userId: '***', // 授权码
});

// 路径箭头 svg
var svgXML =
`<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M529.6128 512L239.9232 222.4128 384.7168 77.5168 819.2 512 384.7168 946.4832 239.9232 801.5872z" p-id="9085" fill="#ffffff"></path>
</svg>
`
// 路径箭头 svg 的 base64 编码
var svgBase64 = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgXML)));

map.on('load', function () {
let arrowIcon = new Image(20, 20)
arrowIcon.src = svgBase64
setRouteData()
arrowIcon.onload = function () {
map.addImage('arrowIcon', arrowIcon)
map.loadImage('../example_data/images/car.png', function (error, carIcon) {
if (carIcon) {
map.addImage('carIcon', carIcon);

}
})
}
})
var isPlay = false
var counter = 0
var steps = 0
let aLength = 0;

// 路径数据源
var routeGeoJson = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': [
[120.75236808097003, 31.310988056897628],
[120.75006711891939, 31.310880542461362],
[120.74868167697014, 31.3108417705798],
[120.74729021576223, 31.31079257591918],
[120.74581230326271, 31.310786277780807],
[120.74449414522735, 31.31076119157757],
[120.74266313345686, 31.31072567112811],
[120.74135746183623, 31.310700616956268],
[120.7397494547941, 31.310669251429985],
[120.7391303778071, 31.31065747337675],
[120.73853841576351, 31.31064570802998],
[120.73817347450625, 31.31063898600759],
[120.73756807432216, 31.310627186093285],
[120.73716653963015, 31.31061961783439],
[120.73670444849, 31.310610403706363],
[120.73627458920419, 31.310602394609134],
[120.73448654445474, 31.310567571247944],
[120.73286742922306, 31.310536266512642],
[120.73125262370638, 31.31050455593484],
[120.7293103449951, 31.310467025033176],
[120.72926001060898, 31.31163202234754],
[120.7292280282852, 31.312372549253524],
[120.72918804098197, 31.313307365193836],
[120.72915434328337, 31.31410431548335],
[120.72911457304718, 31.315036305680692],
[120.7290702255334, 31.31604385956301],
[120.72906343423136, 31.31620315260247],
[120.7290433113834, 31.31669343675162],
[120.72901932685613, 31.31726499796291],
[120.72897940050234, 31.31818868795655],
[120.72894864068206, 31.318914513984907],
[120.7289041259636, 31.319955870912196],
[120.72886254512497, 31.32092416043791],
[120.72879451876588, 31.322506893729297],
[120.72871597473073, 31.324318601315653],
[120.728642420607, 31.326063061394436],
[120.72855885463895, 31.328012524260387],
[120.72842847363745, 31.331050201333852],
[120.72831076063807, 31.333773116347103],
[120.72816535017847, 31.337202096141723],
[120.7280410056245, 31.340100166674603],
[120.72845923914889, 31.34016969110995],
[120.72856368588845, 31.33990718642187]
]
}
}]
}

// 实际路径数据源
var RealRouteGeoJson = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': []
}
}]
}

// 车辆动画路径数据源
var AnimatePointGeoJson = {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'Point',
'coordinates': []
}
}]
}

// 获取轨迹数据
function setRouteData() {
AnimatePointGeoJson.features[0].geometry.coordinates = routeGeoJson.features[0].geometry.coordinates[0]
aLength = routeGeoJson.features[0].geometry.coordinates.length;
newRouteGeoJson = ResetRoute(routeGeoJson.features[0], 1000, 'kilometers')
steps = newRouteGeoJson.geometry.coordinates.length

addRoutelayer() // 添加轨迹线图层
addRealRouteSource() // 添加实时轨迹线图层
addArrowlayer() // 添加箭头图层
addAnimatePointSource() // 添加动态点图层
addButtons() // 添加操作按钮
}

// 添加轨迹线图层
function addRoutelayer() {
map.addLayer({
'id': 'routeLayer',
'type': 'line',
'source': {
'type': 'geojson',
'lineMetrics': true,
'data': routeGeoJson
},
'paint': {
'line-width': 10,
'line-opacity': 1,
'line-color': '#009EFF',
}
});
}

// 添加实时轨迹线
function addRealRouteSource() {
map.addLayer({
'id': 'realRouteLayer',
'type': 'line',
'source': {
'type': 'geojson',
'lineMetrics': true,
'data': RealRouteGeoJson
},
'paint': {
'line-width': 10,
'line-opacity': 1,
'line-color': '#FF9900',
}
});
}

// 添加箭头图层
function addArrowlayer() {
map.addLayer({
'id': 'arrowLayer',
'type': 'symbol',
'source': {
'type': 'geojson',
'data': routeGeoJson // 轨迹 geojson 格式数据
},
'layout': {
'symbol-placement': 'line',
'symbol-spacing': 50, // 图标间隔,默认为 250
'icon-image': 'arrowIcon', // 箭头图标
'icon-size': 0.5
}
});
}

// 添加动态点图层
function addAnimatePointSource() {
map.addLayer({
'id': 'animatePointLayer',
'type': 'symbol',
'source': {
'type': 'geojson',
'data': AnimatePointGeoJson
},
'layout': {
'icon-image': 'carIcon',
'icon-size': 0.5,
'icon-rotate': ['get', 'bearing'],
'icon-rotation-alignment': 'map',
'icon-allow-overlap': true,
'icon-ignore-placement': true
}
});

Animate()
}

function addButtons() {
var link = document.getElementById('menuBar');
var button = document.getElementById('menuBar');
button.style.display = 'inline-block';
}

function Animate() {
if (counter >= steps) {
return
}
var startPnt, endPnt
if (counter == 0) {
RealRouteGeoJson.features[0].geometry.coordinates = []
startPnt = newRouteGeoJson.geometry.coordinates[counter]
endPnt = newRouteGeoJson.geometry.coordinates[counter + 1]
} else if (counter !== 0) {
startPnt = newRouteGeoJson.geometry.coordinates[counter - 1]
endPnt = newRouteGeoJson.geometry.coordinates[counter]
}

AnimatePointGeoJson.features[0].properties.bearing = turf.bearing(
turf.point(startPnt),
turf.point(endPnt)
) - 90;
AnimatePointGeoJson.features[0].geometry.coordinates = newRouteGeoJson.geometry.coordinates[counter];
RealRouteGeoJson.features[0].geometry.coordinates.push(AnimatePointGeoJson.features[0].geometry.coordinates)

map.getSource('animatePointLayer').setData(AnimatePointGeoJson);
map.getSource('realRouteLayer').setData(RealRouteGeoJson);
if (isPlay) {
requestAnimationFrame(Animate);
}
counter = counter + 1;
}

function ResetRoute(route, nstep, units) {

var newRoute = {
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': []
}
}
var lineDistance = turf.length(route);
var nDistance = lineDistance / nstep;
for (let i = 0; i < aLength - 1; i++) {
var from = turf.point(route.geometry.coordinates[i]);
var to = turf.point(route.geometry.coordinates[i + 1]);
let lDistance = turf.distance(from, to, {
units: units
});
if (i == 0) {
newRoute.geometry.coordinates.push(route.geometry.coordinates[0])
}
if (lDistance > nDistance) {
let rings = lineMore(from, to, lDistance, nDistance, units)
newRoute.geometry.coordinates = newRoute.geometry.coordinates.concat(rings)
} else {
newRoute.geometry.coordinates.push(route.geometry.coordinates[i + 1])
}
}

return newRoute
}

function lineMore(from, to, distance, splitLength, units) {
var step = parseInt(distance / splitLength)
var leftLength = distance - step * splitLength
var rings = []
var route = turf.lineString([from.geometry.coordinates, to.geometry.coordinates])
for (let i = 1; i <= step; i++) {
let nlength = i * splitLength
let pnt = turf.along(route, nlength, {
units: units
});
rings.push(pnt.geometry.coordinates)
}
if (leftLength > 0) {
rings.push(to.geometry.coordinates)
}
return rings
}

function startClick() {
if (!isPlay) {
isPlay = true
Animate()
}
}

function pauseClick() {
isPlay = false
Animate()
}

function stopClick() {
isPlay = false
counter = 0
Animate()
}
</script>

</body>

</html>