|
|
@ -192,19 +192,85 @@ function calculateDistance(lat1, lon1, lat2, lon2) {
|
|
|
|
return d;
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function shuffle(array) {
|
|
|
|
|
|
|
|
let currentIndex = array.length;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// While there remain elements to shuffle...
|
|
|
|
|
|
|
|
while (currentIndex != 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pick a remaining element...
|
|
|
|
|
|
|
|
let randomIndex = Math.floor(Math.random() * currentIndex);
|
|
|
|
|
|
|
|
currentIndex--;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// And swap it with the current element.
|
|
|
|
|
|
|
|
[array[currentIndex], array[randomIndex]] = [
|
|
|
|
|
|
|
|
array[randomIndex], array[currentIndex]
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function showTravelPoints(indexes) {
|
|
|
|
|
|
|
|
let calculatedPoints = [];
|
|
|
|
|
|
|
|
let prevPoint = null;
|
|
|
|
|
|
|
|
let id = 0;
|
|
|
|
|
|
|
|
for (const i in indexes) {
|
|
|
|
|
|
|
|
const idx = indexes[i];
|
|
|
|
|
|
|
|
const point = points[idx];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (prevPoint != null) {
|
|
|
|
|
|
|
|
const d = (calculateDistance(point.location[0], point.location[1], prevPoint.location[0], prevPoint.location[1]) / 1000.0).toFixed(2);
|
|
|
|
|
|
|
|
calculatedPoints.push({
|
|
|
|
|
|
|
|
ID: id,
|
|
|
|
|
|
|
|
Waypoint: d + ' км.'
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
id++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
calculatedPoints.push({
|
|
|
|
|
|
|
|
ID: id,
|
|
|
|
|
|
|
|
Waypoint: point.text
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
id++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prevPoint = point;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
$(() => {
|
|
|
|
|
|
|
|
$('#waypointsGrid').dxDataGrid({
|
|
|
|
|
|
|
|
dataSource: calculatedPoints,
|
|
|
|
|
|
|
|
showColumnHeaders: false,
|
|
|
|
|
|
|
|
showColumnLines: false,
|
|
|
|
|
|
|
|
showRowLines: true,
|
|
|
|
|
|
|
|
rowAlternationEnabled: true,
|
|
|
|
|
|
|
|
keyExpr: 'ID',
|
|
|
|
|
|
|
|
columns: ['Waypoint'],
|
|
|
|
|
|
|
|
showBorders: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Вычисление оптимального маршрута по начальной и конечной точке
|
|
|
|
// Вычисление оптимального маршрута по начальной и конечной точке
|
|
|
|
function recalculateWaypoints(waypoints) {
|
|
|
|
function recalculateWaypoints(waypoints) {
|
|
|
|
if (!waypoints || waypoints.length < 2) {
|
|
|
|
if (!waypoints || waypoints.length < 1) {
|
|
|
|
DevExpress.ui.notify("Недостаточно точек для построения маршрута");
|
|
|
|
DevExpress.ui.notify("Недостаточно точек для построения маршрута");
|
|
|
|
|
|
|
|
} else if (waypoints.length == 1) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Одна точка, просто ее выводим
|
|
|
|
|
|
|
|
showTravelPoints([waypoints[0].id]);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
var start = waypoints[0];
|
|
|
|
var start = waypoints[0];
|
|
|
|
var end = waypoints[waypoints.length - 1];
|
|
|
|
var end = waypoints[waypoints.length - 1];
|
|
|
|
DevExpress.ui.notify("Расчет маршрута от '" + start.text + "' к '" + end.text + "'");
|
|
|
|
DevExpress.ui.notify("Расчет маршрута от '" + start.text + "' к '" + end.text + "'");
|
|
|
|
if (waypoints.length == 2) {
|
|
|
|
if (waypoints.length == 2) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Две точки, старт и стоп, выводим без расчета
|
|
|
|
|
|
|
|
showTravelPoints([waypoints[0].id, waypoints[1].id]);
|
|
|
|
} else if (waypoints.length == 3) {
|
|
|
|
} else if (waypoints.length == 3) {
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Три точки, старт, стоп и промежуточная выводим без расчета
|
|
|
|
|
|
|
|
showTravelPoints([waypoints[0].id, waypoints[1].id, waypoints[2].id]);
|
|
|
|
|
|
|
|
} else if (waypoints.length <= 9) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Если точек от 4 до 9, используем алгоритм коммивояжера
|
|
|
|
// Создаем словарь с вершинами
|
|
|
|
// Создаем словарь с вершинами
|
|
|
|
let vertexes = {};
|
|
|
|
let vertexes = {};
|
|
|
|
for (const ind in waypoints) {
|
|
|
|
for (const ind in waypoints) {
|
|
|
@ -221,9 +287,17 @@ function recalculateWaypoints(waypoints) {
|
|
|
|
graph.addEdge(new GraphEdge(vertexes[i], vertexes[j], d));
|
|
|
|
graph.addEdge(new GraphEdge(vertexes[i], vertexes[j], d));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Вычисляем Гамильтонов цикл для графа
|
|
|
|
// Решение задачи коммивояжера
|
|
|
|
const hamiltonianCycleSet = hamiltonianCycle(graph);
|
|
|
|
const salesmanPath = bfTravellingSalesman(graph);
|
|
|
|
alert(hamiltonianCycleSet.length);
|
|
|
|
let indexes = [];
|
|
|
|
|
|
|
|
for (var ind in salesmanPath) {
|
|
|
|
|
|
|
|
indexes.push(salesmanPath[ind].value);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
showTravelPoints(indexes);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Если точек от 9, близкие к np алгоритмам расчеты слишком затратные
|
|
|
|
|
|
|
|
// применяем лучшие поиск из 200.000 лучших вариантов (9! = 362880, берем такого порядка число чтобы не грузить браузер расчетами)
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|