3

Tính toán tuyến đường ngắn nhất với API HERE và Vue.js

Con đường nào là nhanh nhất? Hãy xây dựng một ứng dụng Vue.js để tìm hiểu.

Khi nói đến định tuyến, biết cách đi từ điểm A đến điểm B theo cách hiệu quả nhất không phải là trường hợp sử dụng duy nhất. Khi nói đến các ứng dụng hoặc ca sử dụng hiện đại, có thể và cần phải hoàn thành nhiều điều hơn nữa.

Hãy xem kịch bản của một dịch vụ chuyển nhà trọn gói. Khi nói đến tài xế giao hàng, họ chất đầy xe tải của mình tại nhà kho và phải điều hướng đến mọi địa chỉ tồn tại trên một kiện hàng trong xe tải của họ trước khi trả xe tải về nhà kho. Người lái xe không thể chỉ chọn các gói hàng ngẫu nhiên từ xe tải và giao chúng một cách ngẫu nhiên vì điều đó sẽ rất kém hiệu quả. Thay vào đó, mỗi lần giao hàng nên được lên kế hoạch cẩn thận để tối ưu hóa số lượng kiện hàng có thể được giao nhanh nhất.

Vì vậy, với X số địa chỉ ngẫu nhiên, bạn lập kế hoạch lộ trình của mình như thế nào để hiệu quả là ưu tiên?

Thay vào đó, bạn muốn tìm khoảng cách trong Google Maps? Đọc tính toán khoảng cách bằng Google Maps trong ASP.NET MVC

Đây là lúc API mở rộng trình tự theo Waypoint của ĐÂY phát huy tác dụng. Cung cấp cho nó một loạt các vị trí và nó sẽ trả về thứ tự mà các vị trí đó sẽ được điều hướng đến. Sau đó, bạn có thể lấy chuỗi có thứ tự đó và tính toán tuyến đường thực tế giữa chúng.

Chúng ta sẽ khám phá API trình tự Waypoint trong hướng dẫn này bằng cách sử dụng khung JavaScript Vue.js.

Để có ý tưởng về những gì chúng tôi muốn đạt được, hãy lấy hình ảnh động sau:

Tính toán tuyến đường ngắn nhất với API HERE và Vue.js

Voilá, ứng dụng đo điểm tham chiếu của bạn.

Những gì bạn không thể thấy trong hình ảnh trên là tôi đã cung cấp một loạt các vị trí ngẫu nhiên. Sau khi nhận được một chuỗi có thứ tự từ Waypoint Sequence API, các số được gắn vào mỗi điểm đánh dấu để thể hiện thứ tự truy cập. Sau đó, một tuyến đường được tính toán và vẽ giữa các điểm đánh dấu.

Tất cả điều này đã được thực hiện bằng cách sử dụng Leaflet.js với Vue.js và một thư viện hoạt ảnh polyline lạ mắt.

Tạo một dự án Vue.js mới với Vue CLI

Bởi vì đây sẽ là một ví dụ Vue.js, một dự án mới cần được tạo với Vue CLI. Giả sử CLI đã được cài đặt, hãy thực hiện như sau:

vue create wse-project


Chúng tôi vẫn chưa rõ ràng. Vì chúng tôi sẽ thực hiện các yêu cầu HTTP tới các API HERE , chúng tôi sẽ cần cài đặt một gói cụ thể để hoàn thành công việc. Từ CLI, thực hiện như sau:

npm install axios --save


Chúng tôi sẽ sử dụng thư viện axios để thực hiện các yêu cầu HTTP . Có rất nhiều cách khác để làm điều này, nhưng axios làm cho nó rất sạch. Nếu bạn muốn tìm hiểu về các cách khác để thực hiện yêu cầu HTTP bằng JavaScript, hãy xem hướng dẫn trước của tôi có tiêu đề, Thực thi yêu cầu HTTP trong ứng dụng JavaScript .

Không cần thêm mô-đun NPM nào nữa, nhưng chúng tôi sẽ cần một số phụ thuộc vào trình duyệt. Mở tệp public / index.html của dự án và bao gồm các thông tin sau:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="<%= BASE_URL %>favicon.ico">
        <title>WSE Project</title>
        <link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
    </head>
    <body>
        <noscript>
            <strong>We're sorry but the app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
        </noscript>
        <div id="app"></div>
        <!-- built files will be auto injected -->
        <script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/leaflet.polyline.snakeanim@0.2.0/L.Polyline.SnakeAnim.min.js"></script>
    </body>
</html>


Bạn sẽ nhận thấy một số điều trong HTML trên. Chúng tôi đang thêm cả phụ thuộc CSS và JavaScript của Leaflet.js cũng như phụ thuộc hoạt ảnh con rắn để cung cấp cho chúng tôi một hoạt ảnh đa đường đẹp mắt.

Tại thời điểm này, chúng tôi có thể bắt đầu phát triển thành phần của mình.

Thiết kế Thành phần Leaflet.js cho Bản đồ và Chức năng Vị trí

Để giữ cho mã của chúng tôi sạch sẽ và có thể tái sử dụng, chúng tôi sẽ tạo một thành phần Vue riêng cho mọi thứ liên quan đến bản đồ và vị trí. Trong thư mục src / components của dự án, tạo một tệp LeafletMap.vue với mã soạn sẵn sau:

<template>
    <div>
        <div ref="map" style="width: 100vw; height: 100vh;"></div>
    </div>
</template>

<script>
    import axios from "axios";
    export default {
        name: "LeafletMap",
        data() {
            return {
                platform: {},
                map: {},
                sequenceMarkerGroup: {}
            }
        },
        props: {
            appId: String,
            appCode: String,
            latitude: String,
            longitude: String
        },
        created() { },
        async mounted() { },
        methods: {
            dropMarker(position, text) { },
            async calculateRouteSequence(places) { },
            drawRoute(sequence) { }
        }
    }
</script>

<style></style>


Bạn sẽ nhận thấy trong đoạn mã trên rằng chúng ta có một số thuộc tính, một số phương thức và một vài sự kiện vòng đời là một phần của Vue. Các Propsthuộc tính sẽ đại diện cho các thuộc tính được truyền khi thành phần được tạo, mountedphương thức sẽ tải bản đồ khi ứng dụng khởi động và mỗi phương thức sẽ giúp chúng ta với Trình tự điểm và bản vẽ của chúng ta.

Rất nhiều đoạn mã trên đã được khám phá trong các hướng dẫn khác mà tôi đã viết về chủ đề Leaflet.js.

Đầu tiên chúng ta hãy vẽ bản đồ của chúng ta. Trong mountedphương pháp này, hãy bao gồm những điều sau:

async mounted() {
    const tiles = "https://1.base.maps.api.here.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/512/png8?app_id={appId}&app_code={appCode}";
    this.map = new L.Map(this.$refs.map, {
        center: [this.latitude, this.longitude],
        zoom: 10,
        layers: [L.tileLayer(tiles, { appId: this.appId, appCode: this.appCode })]
    });
},


Để sử dụng Tờ rơi, chúng ta cần cung cấp cho nó các ô bản đồ. Bằng cách sử dụng API Ngói bản đồ HERE, mã ứng dụng và mã ứng dụng hợp lệ từ Cổng thông tin nhà phát triển HERE , chúng tôi có thể định cấu hình bản đồ của mình.

Hãy lưu ý rằng this.$refs.maptham số tham chiếu đến refthuộc tính được tìm thấy trong <template>khối.

Để sử dụng thành phần này, hãy mở tệp src / App.vue của dự án và bao gồm các phần sau:

<template>
    <div id="app">
        <LeafletMap
            ref="map"
            appId="APP_ID_HERE"
            appCode="APP_CODE_HERE"
            latitude="37.7297"
            longitude="-121.4252"
        />
    </div>
</template>

<script>
    import LeafletMap from './components/LeafletMap.vue'

    export default {
        name: 'app',
        components: {
            LeafletMap
        },
        async mounted() {
            let map = this.$refs.map;
        }
    }
</script>

<style>
    body {
        margin: 0;
    }
</style>


Trong đoạn mã trên, chúng tôi đang nhập LeafletMaplớp và bao gồm nó với các thuộc tính cần thiết để hiển thị. Chúng ta nên tăng tốc ngay bây giờ khi nói đến nền tảng Leaflet.js.

Tính trình tự điểm tham chiếu với các yêu cầu HERE và HTTP

Trọng tâm của hướng dẫn này là về API trình tự điểm tham chiếu của HERE và tính toán trình tự các điểm tham chiếu cho một tuyến đường tối ưu. Trước khi bắt đầu chơi với API, hãy xác định một loạt các vị trí sẽ được sử dụng.

Trong tệp src / App.vue , thêm phần sau vào mountedhàm:

let mockPlaces = [
    { latitude: 37.74682893940135, longitude: -121.4198684692383 },
    { latitude: 37.73488314788311, longitude: -121.44561767578126 },
    { latitude: 37.72076290898376, longitude: -121.41712188720705 },
    { latitude: 37.74251196215947, longitude: -121.435 },
    { latitude: 37.731793096495316, longitude: -121.41770044283479 },
    { latitude: 37.74, longitude: -121.46 },
    { latitude: 37.72, longitude: -121.455 }
];


Trên đây chỉ là một loạt các vị trí không theo thứ tự cụ thể và tồn tại ở đâu đó xung quanh điểm trung tâm của bản đồ của tôi. Bây giờ chúng ta đã có một số dữ liệu, hãy sử dụng API.

Trong calculateRouteSequencechức năng của tệp src / components / LeafletMap.vue của dự án , hãy bao gồm những điều sau:

async calculateRouteSequence(places) {
    let waypoints = {};
    for(let i = 0; i < places.length; i++) {
        waypoints["destination" + (i + 1)] = `${places[i].latitude},${places[i].longitude}`;
    }
    return axios({
        "method": "GET",
        "url": "https://wse.api.here.com/2/findsequence.json",
        "params": {
            "start": `${this.latitude},${this.longitude}`,
            ...waypoints,
            "end": `${this.latitude},${this.longitude}`,
            "mode": "fastest;car;traffic:enabled",
            "departure": "now",
            "app_id": this.appId,
            "app_code": this.appCode
        }
    }).then(response => {
        return response.data.results[0].waypoints
    });
},


Các placestham số đại diện cho mockPlacesmảng mà chúng ta vừa tạo ra. Chúng tôi cần định dạng đúng dữ liệu đó theo cách mà API mong đợi, vì vậy chúng tôi lặp qua mảng và định dạng nó giống như sau:

waypoints: {
    "destination0": "...",
    "destination1": "...",
    "destination2": "...",
}


Khi chúng ta có tất cả các điểm tham chiếu được định dạng đúng theo thứ tự cụ thể, chúng ta có thể đưa ra yêu cầu với thư viện axios. Vị trí bắt đầu và kết thúc có thể đại diện cho nhà kho mà chúng tôi đang bắt đầu và nhà kho mà chúng tôi cần quay trở lại sau khi tất cả các gói hàng của chúng tôi được giao.

Phản hồi cho yêu cầu của chúng tôi sẽ là chuỗi các điểm tham chiếu được sắp xếp theo thứ tự của chúng tôi.

Để sử dụng calculateRouteSequencehàm, hãy truy cập tệp src / App.vue của dự án và bao gồm các nội dung sau trong mountedhàm:

let sequence = await map.calculateRouteSequence(mockPlaces);


Có trình tự dưới dạng dữ liệu thô không đặc biệt thú vị. Thay vào đó, chúng ta có thể vẽ các điểm đánh dấu tại mỗi điểm trong dãy và cho chúng một con số. Bằng cách này, chúng ta có thể thấy điểm đánh dấu nào đại diện cho điểm dừng trên đường dẫn của chúng ta.

Trong tệp src / components / LeafletMap.vue của dự án , thêm phần sau vào dropMarkerphương thức:

dropMarker(position, text) {
    let icon = L.divIcon({
        html: `
            <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
                <circle cx="25" cy="25" r="25" fill="#000000" />
                <text x="50%" y="50%" text-anchor="middle" fill="white" font-size="25px" dy=".3em">${text}</text>
            </svg>
        `.trim()
    });
    L.marker([position.lat, position.lng], { icon: icon }).addTo(this.sequenceMarkerGroup);
    this.sequenceMarkerGroup.addTo(this.map);
    this.map.fitBounds(this.sequenceMarkerGroup.getBounds());
},


Tờ rơi không có cách tốt để thêm văn bản vào điểm đánh dấu trừ khi bạn đang sử dụng điểm đánh dấu SVG. Vì lý do đó, chúng ta có thể lấy một vị trí và một số văn bản, tạo một SVG động, sau đó thêm nó vào bản đồ.

Bạn nên sử dụng một nhóm điểm đánh dấu để các điểm đánh dấu có thể được căn giữa. Bạn có thể khởi tạo nhóm điểm đánh dấu của mình trong createdphương pháp:

created() {
    this.sequenceMarkerGroup = new L.featureGroup();
},


Để loại bỏ các điểm đánh dấu này, chúng ta cần truy cập lại tệp src / App.vue của dự án :

for(let i = 0; i < sequence.length - 1; i++) {
    map.dropMarker(sequence[i], i.toString());
}


Bởi vì điểm bắt đầu và điểm kết thúc giống nhau, chúng tôi sẽ không lặp lại cho đến điểm cuối cùng, nếu không, điểm đánh dấu bắt đầu của chúng ta sẽ được thay thế bằng điểm đánh dấu kết thúc. Không phải là một vấn đề lớn, nhưng nó có thể gây nhầm lẫn khi nhìn vào.

Bây giờ chúng tôi đã phát hiện ra trình tự tốt nhất, chúng tôi có thể hướng tới việc điều hướng thực sự giữa các điểm tham chiếu này.

Tính toán đường dẫn giữa các điểm tham chiếu với API định tuyến HERE

Để tính toán một tuyến đường có thể được điều hướng, API định tuyến HERE tiêu chuẩn có thể được sử dụng.

Trong tệp src / components / LeafletMap.vue của dự án , thêm phần sau vào drawRoutehàm:

drawRoute(sequence) {
    let waypoints = {};
    for(let i = 0; i < sequence.length; i++) {
        waypoints["waypoint" + i] = `${sequence[i].lat},${sequence[i].lng}`;
    }
    axios({
        "method": "GET",
        "url": "https://route.api.here.com/routing/7.2/calculateroute.json",
        "params": {
            "mode": "fastest;car;traffic:enabled",
            "representation": "display",
            ...waypoints,
            "app_id": this.appId,
            "app_code": this.appCode
        }
    }).then(result => {
        let shape = result.data.response.route[0].shape;
        let line = shape.map(point => {
            let [lat, lng] = point.split(",");
            return { lat: lat, lng: lng };
        });
        new L.Polyline(line, {snakingSpeed: 500}).addTo(this.map).snakeIn();
    }, error => {
        console.error(error);
    });
}


Những gì đang xảy ra trong drawRoutehàm không khác quá nhiều so với những gì đang xảy ra trong calculateRouteSequencehàm. Đầu tiên, dữ liệu trình tự được định dạng và nó được thêm vào yêu cầu axios tới API.

Kết quả tính toán tuyến đường sẽ bao gồm các điểm có thể được thêm vào đường đa tuyến và được hiển thị trên bản đồ. Chúng tôi đang sử dụng thư viện hoạt hình con rắn ở đây để tạo ra một hình ảnh động đẹp mắt cho lộ trình dài của chúng tôi từ đầu đến cuối.

Để sử dụng drawRoutechức năng này , chúng ta có thể thêm nó vào tệp src / App.vue của dự án như sau:

map.drawRoute(sequence);


Như vậy, chúng tôi có một tuyến đường được tính toán tốt nhất cho tài xế của chúng tôi hoặc bất kỳ ai có thể cần một tuyến đường được tối ưu hóa giữa các điểm tham chiếu.

Phần kết luận

Các API Waypoint Chuỗi mở rộng mà ĐÂY Mời là rất mạnh mẽ và là một ứng cử viên tuyệt vời cho việc giải quyết "đi du lịch nhân viên bán hàng" vấn đề mà rất nhiều các tổ chức mặt. Mặc dù chúng tôi không cần sử dụng Tờ rơi, nhưng tôi cảm thấy thư viện hoạt ảnh cho các đường đa tuyến đã mang lại một hình dung tuyệt vời cho những gì chúng tôi đang cố gắng giải quyết.

Đọc thêm

Lập kế hoạch Đường đi cho Xe tự hành hoạt động như thế nào?

Phát hiện đường dẫn tối ưu với học tập củng cố

|