leaflet室内地图规划路线方案

332 阅读3分钟

前段时间,项目用leaflet做室内导航规划路线,用户选择起始点和终点,用地图规划最短路线,并避开障碍物。

image.png

方案一:使用pathfinding寻路插件

Pathfinding 它提供了多种经典的寻路算法,如A*、Dijkstra等,旨在简化在网格环境中实现物体移动路径规划的过程。在学习 PathFinding.js 之前,可以先体验一下它的能力。

体验地址可以看出其查找的效果: image.png

安装

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();