mirror of
https://github.com/KeymonSoft/Ruteador-NonUI.git
synced 2026-04-17 21:06:16 +00:00
- Se agregó el HASH al JSON que regresa cuando se genera el ruteo.
270 lines
12 KiB
JavaScript
270 lines
12 KiB
JavaScript
const CONVERT_TO_RADIAN_CONST = 1; // 0.0174533;
|
|
|
|
function loadScript() {
|
|
var script = document.createElement("script");
|
|
script.type = "text/javascript";
|
|
script.src = "https://maps.googleapis.com/maps/api/js?key=" + config["apiKey"] + "&callback=initMap&v=weekly";
|
|
script.defer = true;
|
|
document.body.appendChild(script);
|
|
}
|
|
|
|
function initMap() {
|
|
let waypointsJSON = localStorage.getItem("waypoints");
|
|
|
|
// let waypointsJSON = [{"name":"Lago Chalco 47, Anáhuac I Secc, Miguel Hidalgo, 11320 Ciudad de México, CDMX, Mexico","lat":0.33943361448716003,"lon":-1.73094628692019},{"name":"C. Lago Chapala 47, Anáhuac I Secc., Anáhuac I Secc, Miguel Hidalgo, 11320 Ciudad de México, CDMX, Mexico","lat":0.33939502873152005,"lon":-1.73093370832688},{"name":"Lago Texcoco, Anáhuac I Secc, Ciudad de México, CDMX, Mexico","lat":0.33941341578307005,"lon":-1.73099749490239},{"name":"Lago Cuitzeo, Anáhuac I Secc., 11320 Ciudad de México, CDMX, Mexico","lat":0.33936825362399003,"lon":-1.73091535792726}]
|
|
let returnToOrigin = localStorage.getItem("returnToOrigin");
|
|
let waypoints = JSON.parse(waypointsJSON);
|
|
// console.log(">>>>>> ga/waypoints", waypointsJSON);
|
|
// console.log(waypoints);
|
|
|
|
const map = new google.maps.Map(document.getElementById("map"), {
|
|
center: { lat: waypoints[0].lat / CONVERT_TO_RADIAN_CONST, lng: waypoints[0].lon / CONVERT_TO_RADIAN_CONST},
|
|
zoom: 8,
|
|
});
|
|
|
|
class Waypoint{
|
|
constructor(name, location) {
|
|
this.name = name;
|
|
this.lat = location.lat();
|
|
this.lon = location.lng();
|
|
}
|
|
}
|
|
|
|
var poly = new google.maps.Polyline({
|
|
editable: true,
|
|
path: []
|
|
});
|
|
|
|
let popSize = config["popSize"];
|
|
let numIterations = config["numIterations"];
|
|
let mutChance = config["mutChance"];
|
|
|
|
// Fisher-Yates shuffle algorithm
|
|
function shuffle(individual) {
|
|
let i = individual.length;
|
|
while (--i > 0) {
|
|
let temp = Math.floor(Math.random() * (i + 1));
|
|
[individual[temp], individual[i]] = [individual[i], individual[temp]];
|
|
}
|
|
}
|
|
|
|
// Generate initial population
|
|
function genInitialPopulation(population) {
|
|
let individual = []; //Arrego con los puntos a visitar.
|
|
let zero = [0];
|
|
for(i=0; i < waypoints.length; ++i){
|
|
individual.push(i); // Agregamos el primer individuo con el orden original de los puntos.
|
|
}
|
|
population.push(individual)
|
|
for (let i = 0; i < popSize; ++i) { // Agregamos a la poblacion el numero de individuos establecido (popSize).
|
|
individual = [...Array(waypoints.length - 1).keys()].map(j => ++j); // Quitamos el 0 (punto de origen) porque es el inicio de la ruta, solo barajeamos los demas y luego lo regresamos.
|
|
shuffle(individual); // Barajeamos los puntos del individuo para tener un individuo random.
|
|
//console.log(">>>>> ga/individual ", i, [...Array(waypoints.length - 1).keys()].map(j => ++j), individual)
|
|
population.push(zero.concat(individual)); // Regresamos el 0 (punto de origen) al inicio de la ruta.
|
|
}
|
|
// console.log(">>>>> ga/population ", population)
|
|
}
|
|
|
|
// Calculate the Haversine distance between two waypoints
|
|
function getHaversineDistance(waypoint1, waypoint2) {
|
|
let dlon = waypoint2.lon - waypoint1.lon;
|
|
let lat1 = waypoint1.lat;
|
|
let lat2 = waypoint2.lat;
|
|
let dlat = lat2 - lat1;
|
|
let a = Math.pow(Math.sin(dlat/2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon/2), 2);
|
|
let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
return 3961 * c;
|
|
}
|
|
|
|
function calcTotalDistance(waypoints, individual) {
|
|
let totalDistance = 0;
|
|
for (let i = 0; i < individual.length - 1; ++i) {
|
|
totalDistance += getDistanceFromLatLonInKm(waypoints[individual[i]], waypoints[individual[i+1]]);
|
|
}
|
|
// Add distance back to origin if returnToOrigin is set to true
|
|
return returnToOrigin === "true" ? totalDistance + getHaversineDistance(waypoints[0], waypoints[individual[individual.length - 1]]) : totalDistance;
|
|
}
|
|
|
|
function getDistanceFromLatLonInKm(waypoint1, waypoint2) {
|
|
let lat1 = waypoint1.lat;
|
|
let lon1 = waypoint1.lon;
|
|
let lat2 = waypoint2.lat;
|
|
let lon2 = waypoint2.lon;
|
|
var R = 6371; // Radius of the earth in km
|
|
var dLat = deg2rad(lat2-lat1); // deg2rad below
|
|
var dLon = deg2rad(lon2-lon1);
|
|
var a =
|
|
Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
|
|
Math.sin(dLon/2) * Math.sin(dLon/2)
|
|
;
|
|
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
var d = R * c; // Distance in km
|
|
return d;
|
|
}
|
|
|
|
function deg2rad(deg) {
|
|
return deg * (Math.PI/180)
|
|
}
|
|
|
|
function normalize(probabilities) {
|
|
let sum = probabilities.reduce(function(a, b) { //Suma todos los valores del arreglo.
|
|
return a + b;
|
|
}, 0);
|
|
// let x = 0;
|
|
// probabilities.forEach((probability, index) => {
|
|
// x += probabilities[index];
|
|
// });
|
|
// console.log("##### SUM: ",sum, x);
|
|
probabilities.forEach((probability, index) => { // Cambia cada valor del arreglo con su valor dividido entre SUM.
|
|
probabilities[index] /= sum;
|
|
});
|
|
}
|
|
|
|
function getRandomInclusive() { // Genera un numero ramdom entro 0 y 1, PERO si resulta 0, entonces regresa 1.
|
|
return Math.random() == 0 ? 1 : Math.random();
|
|
}
|
|
|
|
function getRandomIntInclusive(min, max) {
|
|
min = Math.ceil(min); // Redondeamos hacia arriba.
|
|
max = Math.floor(max); // Redondeamos hacia abajo.
|
|
return Math.floor(Math.random() * (max - min + 1)) + min; // Redondeamos hacia abajo (random * (max - min + 1)).
|
|
}
|
|
|
|
function genNewPopulation(newPopulation, crossoverIndex, individual1, individual2) {
|
|
let newIndividual = [];
|
|
++crossoverIndex;
|
|
for (let i = 0; i < crossoverIndex; ++i) {
|
|
newIndividual.push(individual1[i]);
|
|
}
|
|
for (let i = 0; i < individual2.length; ++i) {
|
|
if (!newIndividual.includes(individual2[i])) {
|
|
newIndividual.push(individual2[i]);
|
|
}
|
|
}
|
|
let random = getRandomInclusive();
|
|
//console.log(random);
|
|
if (random <= mutChance) {
|
|
let index1 = getRandomIntInclusive(1, newIndividual.length - 1);
|
|
let index2 = getRandomIntInclusive(1, newIndividual.length - 1);
|
|
[newIndividual[index1], newIndividual[index2]] = [newIndividual[index2], newIndividual[index1]];
|
|
}
|
|
newPopulation.push(newIndividual);
|
|
}
|
|
|
|
function addToPath(polyPath, latlng, count) {
|
|
polyPath.push(latlng);
|
|
if (count != waypoints.length+1) {
|
|
new google.maps.Marker({
|
|
position: latlng,
|
|
label: {text: count.toString(), color: "#00FF00"},
|
|
animation: google.maps.Animation.DROP,
|
|
map: map,
|
|
});
|
|
}
|
|
}
|
|
|
|
function startNewCalculation() {
|
|
window.location.href = "index.html";
|
|
}
|
|
document.getElementById("goto-index").addEventListener("click", startNewCalculation);
|
|
let waypointsList = document.getElementById("waypoints-list");
|
|
|
|
/*
|
|
INICIAMOS LOS CALCULOS
|
|
*/
|
|
let population = [];
|
|
|
|
// let fitTemp = [];
|
|
let sortedIndexTemp = [];
|
|
genInitialPopulation(population); // Mandamos population en blanco y regresamos 128 (popSize) variaciones.
|
|
for (let i = 0; i <= numIterations; ++i) {
|
|
// fitness[i] <==> the ith route's total distance
|
|
let fitness = [];
|
|
population.forEach(individual => {
|
|
fitness.push(calcTotalDistance(waypoints, individual)); // Ponemos en fitness la distancia total entre los puntos de este individuo en particular.
|
|
});
|
|
// fitTemp = fitness;
|
|
// console.log(">>>>> ga/fitness", fitness)
|
|
let sortedIndexes = [...Array(popSize).keys()].sort((index1, index2) => {
|
|
return fitness[index1] < fitness[index2] ? -1 : 1;
|
|
});
|
|
// console.log(sortedIndexes);
|
|
|
|
let probabilities = new Array(popSize).fill(1.0 / popSize); // No se que haga este arreglo de probabilidades, pero se usa en algoritmos geneticos.
|
|
probabilities[sortedIndexes[0]] *= 6; // Al parecer tiene que ver con las posibiliddes de que un individuo se escoja para la siguiente generación.
|
|
probabilities[sortedIndexes[1]] *= 6;
|
|
for (let j = 0; j < popSize / 2; ++j) {
|
|
probabilities[sortedIndexes[j]] *= 3;
|
|
}
|
|
// console.log(">>>>> ga/probabilities", probabilities);
|
|
// let probs = [] = probabilities;
|
|
normalize(probabilities);
|
|
|
|
// console.log(calcTotalDistance(waypoints, population[sortedIndexes[0]]))
|
|
|
|
// console.log(">>>>> ga/probabilities normalizadas", probabilities);
|
|
if (i == numIterations) { // Si ya completamos el numero de iteraciones especificadas (numIterations), entonces salimos.
|
|
let solution = population[sortedIndexes[0]];
|
|
// console.log(">>>>> POPULATION: ", population)
|
|
console.log(">>>>> FITNESS: ", fitness)
|
|
// console.log(">>>>> SORTED INDEXES: ", sortedIndexes)
|
|
// console.log(">>>>> PROBS: ", probs);
|
|
// console.log(">>>>> PROBABILITIES: ", probabilities)
|
|
console.log(">>>>> GA/SOLUTION:", solution);
|
|
console.log(calcTotalDistance(waypoints, solution))
|
|
let polyPath = [];
|
|
let count = 0;
|
|
let waypointElement = null;
|
|
solution.forEach(waypointIndex => { // Generamos la polilinea para Google Maps.
|
|
waypoint = waypoints[waypointIndex];
|
|
waypointElement = document.createElement("li");
|
|
waypointElement.append(waypoint.name);
|
|
waypointsList.appendChild(waypointElement);
|
|
addToPath(polyPath, new google.maps.LatLng(waypoint.lat / CONVERT_TO_RADIAN_CONST, waypoint.lon / CONVERT_TO_RADIAN_CONST), ++count);
|
|
});
|
|
if (returnToOrigin === "true") {
|
|
addToPath(polyPath, new google.maps.LatLng(waypoints[0].lat / CONVERT_TO_RADIAN_CONST, waypoints[0].lon / CONVERT_TO_RADIAN_CONST), ++count);
|
|
}
|
|
poly.setPath(polyPath);
|
|
poly.setMap(map);
|
|
break;
|
|
}
|
|
let index1 = 0;
|
|
let index2 = 0;
|
|
let random = 0;
|
|
let currSum = 0;
|
|
let crossoverIndex = 0;
|
|
let aGoesFirst = 0;
|
|
let newPopulation = [];
|
|
for (let j = 0; j < popSize; ++j) {
|
|
currSum = 0;
|
|
random = getRandomInclusive();
|
|
for (let k = 0; k < popSize; ++k) { // Generamos index1.
|
|
currSum += probabilities[k];
|
|
if (currSum >= random) {
|
|
index1 = k;
|
|
break;
|
|
}
|
|
}
|
|
currSum = 0;
|
|
random = getRandomInclusive();
|
|
for (let k = 0; k < popSize; ++k) { // Generamos index2.
|
|
currSum += probabilities[k];
|
|
if (currSum >= random) {
|
|
index2 = k;
|
|
break;
|
|
}
|
|
}
|
|
crossoverIndex = getRandomIntInclusive(1, waypoints.length - 2);
|
|
aGoesFirst = getRandomIntInclusive(0, 1);
|
|
//console.log("****************** ",crossoverIndex, aGoesFirst)
|
|
// Si aGoesFirst = 0 o 1, entonces obtenemos nueva poblacion de una u otra forma!
|
|
aGoesFirst ? genNewPopulation(newPopulation, crossoverIndex, population[index1], population[index2])
|
|
: genNewPopulation(newPopulation, crossoverIndex, population[index2], population[index1]);
|
|
}
|
|
population = newPopulation;
|
|
}
|
|
//console.log(">>>>> FITNESS TEMP: ", fitTemp)
|
|
//console.log(">>>>> SORTED INDEXES TEMP: ", sortedIndexTemp)
|
|
} |