Skip to content
This repository was archived by the owner on Sep 18, 2020. It is now read-only.

Frontend 3.8 Routenalgorithmus erstellen Felix

Markus Thömmes edited this page Jan 30, 2015 · 8 revisions

[← zurück zum implementierten Algorithmus](Frontend 3.8 Routenalgorithmus erstellen)

RouteGeneratorService

Definition: RouteGeneratorService
Modul: happyHour.algorithm.RouteGenerator
Dateiname: components/algorithm/RouteGenerator_Felix.js
Typ: Angular Service
Autor: Felix Rieder

Abhängigkeiten

Google Maps Javascript API v3
Zur Berechnung der Distanzen via Distance Matrix Service. Google zwingt Clients zur Benutzung dieses Codes, für Tracking und ähnliche Zwecke.
time
Für die Bearbeitung von Strings die Uhrzeiten enthalten.

Funktionsweise

Der Algorithmus bezieht alle seine Informationen aus den übergebenen Objekten, dem Array aus Bars und dem Options Objekt.

Intern wichtig ist zusätzlich noch die Information über die Distanzen zwischen den verschiedenen Bars und dem User-Standpunkt. Diese Daten werden über die Google Maps API abgerufen. Hierzu gibt es intern die Hilfsfunktion googleApiDist().

googleApiDist() - Distanzenermittlung

In dieser Funktion wird unter Benutzung der Google Maps Javascript API v3 ein Google DistanceMatrix Service erstellt und damit ein Request zur Ermittlung der Distanzen erstellt.

Als Input nimmt die googleApiDist() Funktion ein Array aus den Startpunkten und den Endpunkten in Form von Longitude und Latitude Werten, die jeweils in einem von Google definierten Objekt gespeichert sind:

var service = new google.maps.DistanceMatrixService();
var barlocs = createArray(jsonbars.length, 2);
for (var o = 0; i < jsonbars.length; i++) {
    barlocs[o][0] = jsonbars[i].location.latitude;
    barlocs[o][1] = jsonbars[i].location.longitude;
}
var loc = [];
loc[0] = options.location.latitude;
loc[1] = options.location.longitude;
// Startpunkt/Standort als location an position 0 in array:
barlocs.splice(0, 0, loc);            
for(var i = 0; i < barlocs.length; i++) {
    barlocs[i] = new google.maps.LatLng(barlocs[i][0], barlocs[i][1]);
}
var origs = barlocs;
var dests = barlocs;
service.getDistanceMatrix({
    origins: origs,
    destinations: dests,
    travelMode: google.maps.TravelMode.WALKING,
    unitSystem: google.maps.UnitSystem.METRIC,
}, callback);

In der callback-Funktion wird dann die Antwort von Google verarbeitet und bei erfolgreicher Abfrage die Daten abgespeichert.

Auswahl der Bar

Der Algorithmus ist hauptsächlich darauf ausgelegt, Happy-Hours zu präferieren. Eine Bar mit einer Happy-Hour und ohne vorhandenes Rating wird einer Bar ohne Happy-Hour mit 5 Punkte Ranking vorgezogen und in den entsprechenden Timeslot gesetzt.

Die Auswahl der Bars für die zur Verfügung stehenden Zeit erfolgt nach folgendem Schema:

  1. Anpassung des Ratings der Bar
  2. Rating der Distanzen
  3. Auswahl der in Frage kommenden Bars anhand der Verfügbarkeit einer Happy-Hour
  4. Auswahl der besten in Frage kommenden Bar
  5. Befüllung des Timeframes im Routen-Objekt

Zur leichteren Sortierung der Bars wird das Rating etwas abgewertet wenn die gewünschte stayTime des Users größer ist als die Dauer der Happy-Hour.

// Dauer der Happy Hour entspricht bar[i][2]
if (bars[i][2] - options.stayTime < 0) {
    // Abwertung des Ratings
}

In einem weiteren Schritt werden die Distanzen der Bars in einen zum Rating passenden Wert gewandelt:
```javascript var total = 0; for (var i = 0; i < n; i++) { total += distances[0][i]; } var rating = []; for (i = 0; i < n; i++) { rating[i] = convertDecimal(distances[0][i] / total); } function convertDecimal(val) { val *= 100; val = Math.floor(val); val = (50 - val) / 10; return val; } ```

Nun wird eine Bar ausgesucht die zur gewünschten Startzeit eine Happy-Hour hat. Durch die Annahme das nahezu immer eine Happy Hour zur Verfügung steht ist es das präferierte Kriterium. Kommen mehrere Bars in Frage wird anhand des Ratings, welches auch die Bewertung der Distanz der Bar enthält, die Beste ausgewählt:

for (var i = 0; i < bars.length; i++) {
    if (bars[i][1] - startTime === 0) {
        // wir haben eine Bar gefunden die passend zur Startzeit der Tour eine HappyHour hat
        possibles[i] = bars[i];
    }
}
// Code..
if (possibles.length > 1) {
    // mehrere Bars kommen als Startbar in Frage!
    // Vergleiche Bars mit gleicher HappyHour anhand Bewertung und Distanz
    var merged = [];
    // gleiche Rating in Bar array an
    for (var k = 0; k < distanceRating.length; k++) {
        var rating = (distanceRating[k] + bars[k][1]) / 2 * 10;
        merged[k][0] = bars[k][0]; // ID string of Bar
        merged[k][1] = rating;
    }
    var temparr = [];
    // Waehle nur die Bars aus, die auch in Frage kommen
    for (var h = 0; h < possibles.length; h++) {
        for (var j = 0; j < bars.length; j++) {
            if (possibles[h][0] === bars[j][0]) {
                temparr.push(merged[j]);
            }
        }
    }
    // Sortiere das array mittels implementierter Array.sort() Funktion mit angepasster Vergleichsfunktion
    // Sortiert wird die zweite Dimension
    temparr.sort(function(a, b) {
        if (a[1] === b[1]) {
            return 0;
        } else {
            return (a[1] < b[1]) ? 1 : -1;
        }
    });
    // Gib die beste Bar zurueck, ueberspringe aber die Erste weil die eigene Position immer die Beste ist und das Rating 5 bekommt beim Distance Rating
    return temparr[1];
} else {
    // wir haben exakt eine Bar die in Frage kommt.
    return possibles[0];
}

Das zurück gegebene Element enthält die ID der Bar. Anhand dieser wird dann die Bar identifiziert und das zugehörige Bar-Objekt in das Routen-Objekt im Timeframe eingebaut:

// chosenBar wurde als beste Bar für den ersten Timeslot ausgewählt. 
for (var z = 0; z < bars.length; z++) {
    if (bars[z].id === chosenBar[0]) {
        var t = getTime(options.startTime);
        var ho = Math.floor(options.stayTime / 60);
        t.add(ho, 'hours');
        t.add(options.stayTime % 60, 'minutes');
        Route.timeframes[0] = {
            'startTime': options.startTime,
            'endTime': t.toString().slice(0, 5),
            'bar': jsonbars[z]
        };
        break;
    }
}

chosenBar ist hier die ausgewählte Startbar. Deswegen wird als Timeframe-Startzeit die vom Benutzer vorgegebene Startzeit gesetzt und die zur stayTime passende Endzeit gesetzt. Bei den folgenden Bars werden die entsprechende Werte berechnet und gesetzt. Ist die komplette Wunschzeit des Users befüllt wird die erstellte Route zurückgegeben.

Clone this wiki locally