在物流系统中有路径规划场景可用osrm路径规划

路径规划部署

version: '3'
services:
  osrm:
    image: osrm/osrm-backend
    container_name: osrm-china
    ports:
      - "5000:5000"
    volumes:
      - D:\data:/data
    command: >
      bash -c "
        osrm-extract -p /opt/car.lua /data/china-latest.osm.pbf &&
        osrm-partition /data/china-latest.osrm &&
        osrm-customize /data/china-latest.osrm &&
        osrm-routed --algorithm mld /data/china-latest.osrm
      "

示例

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>地址生成物流轨迹</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <style>
        body, html { margin: 0; padding: 0; height: 100%; }
        #map { height: 90%; }
        #form {
            height: 10%; padding: 8px; display: flex;
            gap: 10px; align-items: center; background: #f5f5f5;
        }
        input { padding: 6px; flex: 1; position: relative; }
        button { padding: 6px 12px; }

        .suggestions {
            position: absolute;
            background: white;
            border: 1px solid #ccc;
            max-height: 160px;
            overflow-y: auto;
            z-index: 1000;
            width: 100%;
        }
        .suggestions div {
            padding: 6px;
            cursor: pointer;
        }
        .suggestions div:hover {
            background: #eee;
        }
        .input-wrapper {
            position: relative;
            flex: 1;
        }
    </style>
</head>
<body>
<div id="form">
    <div class="input-wrapper">
        <input id="from" placeholder="请输入发货地,如 广州市" oninput="showSuggestions(this, 'from')" />
        <div id="from-suggestions" class="suggestions" style="display:none;"></div>
    </div>
    <div class="input-wrapper">
        <input id="to" placeholder="请输入收货地,如 北京市" oninput="showSuggestions(this, 'to')" />
        <div id="to-suggestions" class="suggestions" style="display:none;"></div>
    </div>
    <button onclick="genRoute()">生成轨迹</button>
</div>
<div id="map"></div>

<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
    const map = L.map('map').setView([30.5, 114.3], 6);
    const gaodeKey = '1111111111111111111111111111111';
    L.tileLayer('https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}&key=' + gaodeKey, {
        attribution: '&copy; 高德地图',
        maxZoom: 18
    }).addTo(map);

    let fromMarker = null, toMarker = null, polyline = null;

    async function geocode(address) {
        const url = `https://restapi.amap.com/v3/geocode/geo?key=${gaodeKey}&address=${encodeURIComponent(address)}&output=JSON`;
        const res = await fetch(url);
        const data = await res.json();
        if (data.status !== '1' || data.geocodes.length === 0) {
            throw new Error(`高德找不到地址: ${address}`);
        }
        const location = data.geocodes[0].location; // "lng,lat"
        const [lng, lat] = location.split(',').map(parseFloat);
        return { lat, lng };
    }

    async function showSuggestions(inputElem, type) {
        const val = inputElem.value.trim();
        const sugBox = document.getElementById(`${type}-suggestions`);

        if (!val) {
            sugBox.style.display = 'none';
            return;
        }

        const url = `https://restapi.amap.com/v3/assistant/inputtips?key=${gaodeKey}&keywords=${encodeURIComponent(val)}&datatype=all&output=JSON`;
        const res = await fetch(url);
        const data = await res.json();

        sugBox.innerHTML = '';
        if (data.tips && data.tips.length > 0) {
            data.tips.forEach(tip => {
                if (tip.name) {
                    const div = document.createElement('div');
                    div.textContent = tip.name;
                    div.onclick = () => {
                        inputElem.value = tip.name;
                        sugBox.style.display = 'none';
                    };
                    sugBox.appendChild(div);
                }
            });
            sugBox.style.display = 'block';
        } else {
            sugBox.style.display = 'none';
        }
    }

    async function genRoute() {
        const fromInput = document.getElementById('from').value.trim();
        const toInput = document.getElementById('to').value.trim();

        if (!fromInput || !toInput) {
            alert('请输入发货地和收货地');
            return;
        }

        try {
            const from = await geocode(fromInput);
            const to = await geocode(toInput);

            if (fromMarker) map.removeLayer(fromMarker);
            if (toMarker) map.removeLayer(toMarker);
            if (polyline) map.removeLayer(polyline);

            fromMarker = L.marker([from.lat, from.lng]).addTo(map).bindPopup('发货地').openPopup();
            toMarker = L.marker([to.lat, to.lng]).addTo(map).bindPopup('收货地');

            const fromStr = `${from.lng},${from.lat}`;
            const toStr = `${to.lng},${to.lat}`;

            const res = await fetch(`map?from=${fromStr}&to=${toStr}`);
            const data = await res.json();

            const latlngs = data.map(p => [p.lat, p.lng]);
            polyline = L.polyline(latlngs, { color: 'blue' }).addTo(map);
            map.fitBounds(latlngs);
        } catch (err) {
            alert(err.message);
            console.error(err);
        }
    }

    // 关闭建议框(点击外部)
    document.addEventListener('click', (e) => {
        if (!e.target.closest('.input-wrapper')) {
            document.querySelectorAll('.suggestions').forEach(el => el.style.display = 'none');
        }
    });
</script>
</body>
</html>

路径规划调用

public function map(){
    // 起点和终点经纬度,格式:lng,lat
    $from = $_GET['from'] ?? '113.2644,23.1291'; // 广州
    $to = $_GET['to'] ?? '116.4074,39.9042';    // 北京
    //http://服务地址:15000/route/v1/driving
    $osrm = config("OSRM");
    $url = $osrm.'/'.$from.';'.$to."?overview=full&geometries=geojson";
    $response = file_get_contents($url);
    $data = json_decode($response, true);

    if (!isset($data['routes'][0]['geometry']['coordinates']))
    {
        http_response_code(500);
        echo json_encode(['error' => '路径规划失败']);
        exit;
    }

    $coords = $data['routes'][0]['geometry']['coordinates'];
    $baseTime = time();

    $track = [];
    foreach ($coords as $i => $pt) {
        $track[] = [
            'time' => date('Y-m-d H:i:s', $baseTime + $i * 1800),
            'lng' => $pt[0],
            'lat' => $pt[1],
            'status' => $i == 0 ? '发货' : ($i == count($coords) - 1 ? '签收' : '运输中')
        ];
    }

    header('Content-Type: application/json');
    echo json_encode($track, JSON_UNESCAPED_UNICODE);
}