前段时间,项目用leaflet做室内导航规划路线,用户选择起始点和终点,用地图规划最短路线,并避开障碍物。
方案一:使用pathfinding寻路插件
Pathfinding 它提供了多种经典的寻路算法,如A*、Dijkstra等,旨在简化在网格环境中实现物体移动路径规划的过程。在学习 PathFinding.js 之前,可以先体验一下它的能力。
- 体验地址:qiao.github.io/PathFinding…
PathFinding.js仓库:github.com/qiao/PathFi…
体验地址可以看出其查找的效果:
安装
npm install pathfinding
如何在leaft中使用
import L from "leaflet"
import "leaflet/dist/leaflet.css"
import * as PF from "pathfinding"
import 'pathfinding/visual/css/style.css'
//首先定义一个地图
var map = L.map('map').setView([51.505, -0.09], 14);
//设置障碍物
let obstacles = [
[
[51.505, -0.10],
[51.51, -0.12],
[51.52, -0.08]
],
[
[51.51, -0.07],
[51.53, -0.06],
[51.52, -0.05]
],
[
[51.50, -0.05],
[51.49, -0.06],
[51.48, -0.04]
]
];
// 将障碍物绘制到地图上
obstacles.forEach(coords => L.polygon(coords, {
color: 'red'
}).addTo(map));
//设置开始坐标和结束坐标
let startCoords = [51.5, -0.09];
let endCoords = [51.53, -0.04];
//在地图上标点
let startMarker = L.marker(startCoords).addTo(map).bindPopup("Start");
let endMarker = L.marker(endCoords).addTo(map).bindPopup("End");
//设置网格大小
let gridSize = 60;
let grid = new PF.Grid(gridSize, gridSize);
let finder = new PF.AStarFinder();
//将障碍物在网格的位置上标注为false
obstacles.forEach(coords => {
coords.forEach(coord => {
//将地理位置转为canvas坐标轴位置
let [x, y] = latLngToGrid(coord[0], coord[1]);
console.log(11, x, y)
grid.setWalkableAt(x, y, false);
});
});
//查找位置,latLngToGrid将地理位置转坐标轴
let startGrid = latLngToGrid(startCoords[0], startCoords[1]);
let endGrid = latLngToGrid(endCoords[0], endCoords[1]);
let path = finder.findPath(startGrid[0], startGrid[1], endGrid[0], endGrid[1], grid);
//渲染到地图上,gridToLatLng将坐标轴转地理位置
let latLngPath = path.map(p => gridToLatLng(p[0], p[1]));
let polyline = L.polyline(latLngPath, {
color: 'blue',
weight: 4
}).addTo(map);
//latLngToGrid将地理位置转坐标轴
function latLngToGrid(lat, lng) {
let x = Math.floor((lng + 网格开始位置的lat) * gridSize);
let y = Math.floor((lat - 网格开始位置的log) * gridSize);
return [x, y];
}
//gridToLatLng将坐标轴转地理位置
function gridToLatLng(x, y) {
let lat = y / gridSize + 网格开始位置的log;
let lng = x / gridSize - 网格开始位置的lat;
return [lat, lng];
}
方案二:收集所有路径的点位,使用Dijkstra最短路径算法
这个方案需要后端配合,收集的所有十字路口的点、及2个点组成双向的2条线的数组、点和线的、线的距离等储存在数据库中,然后先查找初始点,查找点相关联的线,在用Dijkstra最短路径算法算出最短路径。
一、下载Dijkstra 算法
// Dijkstra 算法实现
export default class Dijkstra {
constructor(graph) {
this.graph = graph.nodes;
}
findShortestPath(start, end) {
const distances = {};
const previous = {};
const queue = new Set();
// 初始化
Object.keys(this.graph).forEach(node => {
distances[node] = Infinity;
previous[node] = null;
queue.add(node);
});
distances[start] = 0;
while (queue.size > 0) {
const current = this.getMinDistanceNode(distances, queue);
queue.delete(current);
if (current === end) break;
let edges = this.graph[current].edges
for (const neighbor in edges) {
const alt = distances[current] + edges[neighbor];
if (alt < distances[neighbor]) {
distances[neighbor] = alt;
previous[neighbor] = current;
}
}
}
let obj = this.reconstructPath(previous, end, distances)
return obj;
}
getMinDistanceNode(distances, queue) {
return Array.from(queue).reduce((minNode, node) =>
distances[node] < distances[minNode] ? node : minNode
);
}
reconstructPath(previous, end, distances) {
const path = [];
let distance = distances[end]
let current = end;
while (current !== null) {
path.unshift(current);
current = previous[current];
if (distances[current]) {
distance += distances[current]
}
}
let paths = path.length > 1 ? path : [];
distance = Math.round(distance / 17)
return {
paths,
distance
}
}
}
二、收集点位的数据结构
//将收集的点组成一下结构方便循环查找
{
"nodes": {
"51.4877511036733,-0.09929538914097649": {
"lng": "-0.09929538914097649",
"lat": "51.4877511036733",
"edges": {
"51.488899060215815,-0.09921501119365853": 127.91
}
},
}
}
三、用最短路径算法查找
//roadData及上面收集所有点线的数据
let astar = new Dijkstra(roadData)
//输入起始点和终点进行查找
let obj = astar.findShortestPath(start, end)
//算法返回的是字符串,拆成数组
let path = obj.paths.map(item => {
return item.split(',')
})
//绘制到地图上
let polygon = L.polyline(path).addTo(map);
//创建轨迹回放
import "leaflet-trackplayer";
track.value = new L.TrackPlayer(path, {
markerIcon,
markerRotation: true,
showMeasurements: true,
speed: speed, // 播放速度
}).addTo(map.value);
track.value.start();