Bing Maps API – Routenplaner
avatar

Wer für sich oder einen Kunden eine eigene Routenplanerkomponente entwickeln möchte, sollte sich Bing Maps mal genauer ansehen. Nicht so populär wie Google Maps, aber eben so gut! Denn wer sich mit der Bing Maps API näher beschäftigt, wird feststellen, dass sich relativ einfach eigenen, tolle Lösungen umsetzen lassen.

Ich habe da mal was vorbereitet. Smile

Über das Backend werden vier Kunden- und Adressdatensätze in die View und weiter in die Bing Map geladen. Rechts neben der Maps werden die ermittelten Wegpunkte mit den jeweiligen Zeiten und Entfernungen aufgelistet. Der Anwender kann diese nun per Drag & Drop umsortieren und die Route neu berechnen. Zum Schluss kann die gewünschte Route zur Speicherung/Verarbeitung an den Server gesendet werden.

image

Mit der Maus kann bspw. Kunde 4 auf Station 2 verschoben werden.

image

Wenn die Route nun neu berechnet wird, ergibt sich eine neue, längere Route von 15,91 Km.

image

 

Für diese Demo wird im Controller eine Liste von Wegepunkten (Kunden) erstellt und an die View übergeben.

Die zweite Methode “SaveTour” nimmt die kalkulierten Routendaten wieder entgegen, um diese im Backend zu verarbeiten.

public ActionResult Index()
        {
            var vm = new AzureWebsite.Models.TourViewModel
            {
                //just some sample data for the view
                WayPoints = new List<Models.TourWayPointModel>
                 {
                    new Models.TourWayPointModel() {
                        Id= 1,
                        Address="Zirkusweg 1, 20359 Hamburg",
                        Name="Kunde 1",
                        Order=1,
                        Minutes=0,
                        Distance=0
                    },
                    new Models.TourWayPointModel() {
                        Id= 2,
                        Address="Spaldingstraße 1, 20097 Hamburg",
                        Name="Kunde 2",
                        Order=2,
                        Minutes=0,
                        Distance=0
                    },
                    new Models.TourWayPointModel() {
                        Id= 3,
                        Address="Hamburger Straße 31, 22083 Hamburg",
                        Name="Kunde 3",
                        Order=3,
                        Minutes=0,
                        Distance=0
                    },
                    new Models.TourWayPointModel() {
                        Id= 4,
                        Address="Dorotheenstraße 1, 22301 Hamburg",
                        Name="Kunde 4",
                        Order=4,
                        Minutes=0,
                        Distance=0
                    }
                 }
            };
            return View(vm);
        }

        public ActionResult SaveTour(AzureWebsite.Models.TourViewModel vm)
        {
            //Do something usefull with the route data
            return RedirectToAction("Index");
        }

 

In der View ist etwas mehr Anwendungslogik erforderlich. Ich habe den Code der Seite in drei Teile geteilt, damit er etwas übersichtlicher ist.

  • Teil 1: Anwendungslogik
  • Teil 2: Umsortieren der Wegepunkte mit Drag & Drop
  • Teil 3: HTML

Der erste Teil mit der Anwendungslogik ist mit Abstand der größte.

Teil 1: Anwendungslogik mit Bing Map und Routenplanermodul (Microsoft.Maps.Directions)

<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
    var directionsManager;
    var map;

    function GetMap() {
        map = new Microsoft.Maps.Map(document.getElementById("myMap"), {
            credentials: 'Your Bing Map Key'
        });

        Microsoft.Maps.loadModule('Microsoft.Maps.Directions', { callback: directionsModuleLoaded });
    }

    function directionsModuleLoaded() {
        directionsManager = new Microsoft.Maps.Directions.DirectionsManager(map);
        var container = document.getElementById('itineraryDiv');
        directionsManager.setRenderOptions({ itineraryContainer: container });

        //Add event handlers to directions manager.
        Microsoft.Maps.Events.addHandler(directionsManager, 'directionsError', directionsError);
        Microsoft.Maps.Events.addHandler(directionsManager, 'directionsUpdated', directionsUpdated);

        // Set Route Mode to driving
        directionsManager.setRequestOptions({
            routeMode: Microsoft.Maps.Directions.RouteMode.driving,
            routeDraggable: false,
            routeAvoidance: [Microsoft.Maps.Directions.RouteAvoidance.avoidHighways],
            distanceUnit: Microsoft.Maps.Directions.DistanceUnit.kilometers,
            avoidTraffic: true
        });

        var waypoints = [];
        @{
            foreach (AzureWebsite.Models.TourWayPointModel waypoint in Model.WayPoints)
            {
                string str = "waypoints.push({ Id:\"" + waypoint.Id.ToString() + "\", Name:\"" + waypoint.Name.Replace(Environment.NewLine, "") + "\", Address:\"" + waypoint.Address + "\", index:\"" + waypoint.Order.ToString() + "\"});";
                WriteLiteral(str);
            }
         }

        //create route using the waypoints
        DirectionComponent.createRoute(waypoints);
    }

    function directionsError(e) {
        alert('Error: ' + e.message + '\r\nResponse Code: ' + e.responseCode)
    }

    // Called on route changed in Map
    function directionsUpdated(e) {
        //Aktualisieren der Arbeitsfläche
        DirectionComponent.refreshEditorPanel(e);
    }

    //custom app logic
    var DirectionComponent = (function (modul) {

        //Create new route by the given waypoints
        modul.createRoute = function (pointsOfRoute) {
            directionsManager.resetDirections({ removeAllWaypoints: true, resetRenderOptions: true });

            //create Bing Maps waypoint for each customer waypoint
            for (i = 0; i < pointsOfRoute.length; i++) {
                let point = pointsOfRoute[i];
                let waypoint = new Microsoft.Maps.Directions.Waypoint({
                    address: point.Address, businessDetails: {
                        businessName: point.Name,
                        entityId: point.Is
                    }, shortAddress: point.Name
                });
                directionsManager.addWaypoint(waypoint);
            }
            //calculate tour
            directionsManager.calculateDirections();
        },
        modul.refreshEditorPanel = function (e) {
            let list = document.getElementById('sortable');
            //initialisiere Inhalt
            list.innerHTML = '';

            let routeIdx = directionsManager.getRequestOptions().routeIndex;

            //Get the distance of the route, rounded to 2 decimal places.
            let distance = Math.round(e.routeSummary[routeIdx].distance * 100) / 100;

            //Get the distance units used to calculate the route.
            let units = directionsManager.getRequestOptions().distanceUnit;
            let distanceUnits = 'miles';

            if (units == Microsoft.Maps.Directions.DistanceUnit.kilimeters) {
                distanceUnits = 'km'
            }

            //Time is in seconds, convert to minutes and round off.
            let time = Math.round(e.routeSummary[routeIdx].timeWithTraffic / 60);
            let panel = document.getElementById('routeInfoPanel');
            let node = document.createElement("P");
            node.innerHTML = 'Distance: ' + distance + ' ' + distanceUnits + '<br/>Time with traffic: ' + time + ' Minutes';
            panel.appendChild(node);

            let routepartsInfos = this.getCurrentRouteInfo();
            let template = "<div id='wayPoint_{{Id}}' >"
             + "<div id='index_{{Index}}' ></div>"
             + "<b>{{Name}}</b></div>"
             + "<div>{{Adresse}}</div>"
             + "<div><b>Time:</b> {{Time}} Minutes</div>"
             + "<div><b>Distance:</b> {{Distance}} Km</div>"
             //Container elemtes for transporting data
             + "<div class='name-item' style='display:None'>{{Name}}</div>"
             + "<div class='id-item' style='display:None'>{{Id}}</div>"
             + "<div class='adress-item' style='display:None'>{{Adresse}}</div>"
             + "</div>"

            let waypoints = directionsManager.getAllWaypoints();

            //fill template
            if (waypoints.length > 0) {
                for (i = 0; i < waypoints.length; i++) {
                    let waypoint = waypoints[i];

                    //use businessdata to store more customer information
                    let businessDetails = waypoint.getBusinessDetails();
                    let itemElem = document.createElement("LI");
                    let displayName = businessDetails.businessName;

                    itemElem.innerHTML = template;
                    itemElem.innerHTML = itemElem.innerHTML.replace(/{{Id}}/g, businessDetails.entityId).replace("{{Index}}", i).replace(/{{Name}}/g, displayName).replace(/{{Adresse}}/g, waypoint.getAddress()).replace("{{IsViaPoint}}", waypoint._isViapoint);
                    if (i - 1 >= 0 && i - 1 < routepartsInfos.length) {
                        itemElem.innerHTML = itemElem.innerHTML.replace("{{Time}}", routepartsInfos[i - 1].time).replace("{{Distance}}", routepartsInfos[i - 1].distance);
                    }
                    else {
                        itemElem.innerHTML = itemElem.innerHTML.replace("{{Time}}", 0).replace("{{Distance}}", 0);
                    }

                    list.appendChild(itemElem);
                }
            }
        },
        //Get information from the route legs
        modul.getCurrentRouteInfo = function () {
            var resultArray = [];
            var routes = directionsManager.getRouteResult();
            // There must be exactly one route
            if (routes && routes.length == 1) {
                var route = routes[0];
                for (i = 0; i < route.routeLegs.length; i++) {
                    let routeLeg = route.routeLegs[i];
                    resultArray.push({
                        time: Math.round(routeLeg.summary.time / 60),
                        distance: (Math.round(routeLeg.summary.distance * 100) / 100)
                    });
                }
            }
            return resultArray;
        },
        //Create route from user order waypoint list
         modul.calculateTour = function () {
             let list = document.getElementById('sortable');
             let reorderArray = [];

             for (i = 0; i < list.children.length; i++) {
                 let li = list.children[i];
                 let wayPointId = $(li).find(".id-item").text();
                 let name = $(li).find(".name-item").text();
                 let address = $(li).find(".adress-item").text();
                 reorderArray.push({ Id: wayPointId, Name: name, Address: address, index: i });
             }
             //start new route calculation
             this.createRoute(reorderArray);
         },
        //Send route data to server for storing
        modul.saveWayPoints = function () {
            //gather route information
            let routeLegInfo = this.getCurrentRouteInfo();
            let waypoints = directionsManager.getAllWaypoints();

            //fill populate data
            let waypointModels = [];
            for (i = 0; i < waypoints.length; i++) {
                let wayPoint = waypoints[i];
                let businessDetails = waypoints[i].getBusinessDetails();

                waypointModels.push({
                    Id: businessDetails.entityId,
                    Order: i,
                    Minutes: this.getMinutes(routeLegInfo, waypoints, wayPoint, i - 1),
                    Distances: this.getDistance(routeLegInfo, waypoints, wayPoint, i - 1),
                    Name: businessDetails.businessName,
                    Address: wayPoint.getAddress()
                });
            }

            let tourModel = {
                Id: parseInt('@Model.Id'),
                WayPoints: waypointModels,
            };

            //send route data to server (storing or what ever)
            this.sendToServer(tourModel);
        },
        modul.sendToServer = function (tourModel) {
            jQuery.ajax({
                type: "POST",
                url: "@Url.Action("SaveTour")",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: JSON.stringify(tourModel),
                success: function (data) { alert(data); },
                failure: function (errMsg) {
                    alert(errMsg);
                }
            });
        },
        modul.getMinutes = function (routeLegInfos, wayPoints, wayPoint, routeLegindex) {
            var min = 0;
            if (routeLegindex >= 0 && routeLegindex < routeLegInfos.length) {
                var wayPointIndex = routeLegindex;
                min = routeLegInfos[routeLegindex].time;
            }
            return min;
        },
        modul.getDistance = function (routeLegInfos, wayPoints, wayPoint, routeLegindex) {
            let dist = 0;
            if (routeLegindex >= 0 && routeLegindex < routeLegInfos.length) {
                let wayPointIndex = routeLegindex;
                dist = routeLegInfos[routeLegindex].distance;
            }
            return dist;
        }

        return modul;
    }(DirectionComponent || {}));

</script>

Teil 2: Die Liste der Wegepunkte sortierbar machen

<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
    $(function () {
        $("#sortable").sortable();
        $("#sortable").disableSelection();
    });

    $(document).ready(function () {
        GetMap();
    });
</script>

Teil 3: HTML-Struktur und CSS

<style>
    #column1 {
        float: left;
        width: 70%;
    }

    #column2 {
        float: left;
        width: 30%;
    }

    .map-container {
        margin-top: 20px;
    }

    #sortable {
        list-style-type: none;
        margin: 0;
        padding: 0;
    }

        #sortable li {
            margin: 0 3px 3px 3px;
            padding: 0.4em;
            padding-left: 1.5em;
            font-size: 1em;
            height: 110px;
            background: #f6f6f6;
            border: 1px solid #c5c5c5;
        }
</style>

<div class="map-container">
    <div id="column1">
        <div id="myMap" style="position: relative; width: 600px; height: 400px;"></div>
    </div>
    <div id="column2">
        <div id='routeInfoPanel'>
            <ul id="sortable"></ul>
        </div>
        <button onclick="DirectionComponent.calculateTour()">Berechnen</button>
        <button onclick="DirectionComponent.saveWayPoints()">Speichern</button>
    </div>
    <div id='itineraryDiv' style="position:relative; width:400px;"></div>
</div>

Ich hoffe der Artikel hat euch gefallen und kann nur jedem empfehlen die Bing Maps API auszuprobieren. Es lohnt sich!

Schreibe einen Kommentar