|
|
@ -1,54 +1,89 @@
|
|
|
|
let markersData = [];
|
|
|
|
// Класс для доступа к данным маршрутов
|
|
|
|
let regions = [];
|
|
|
|
class WaypointsData {
|
|
|
|
let points = [];
|
|
|
|
constructor() {
|
|
|
|
var regionPointsWidget;
|
|
|
|
// Данные для маркеров на карте
|
|
|
|
var selectedPointsWidget;
|
|
|
|
this.markersData = [];
|
|
|
|
const markerUrl = './img/map-marker.png';
|
|
|
|
// Регионы
|
|
|
|
|
|
|
|
this.regions = [];
|
|
|
|
|
|
|
|
// Точки
|
|
|
|
|
|
|
|
this.points = [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Инициализация данных, подготовка массивов для загрузки в контролы
|
|
|
|
// Заполнение данных
|
|
|
|
function initData() {
|
|
|
|
loadData() {
|
|
|
|
for (var r_idx in data['regions']) {
|
|
|
|
let point_counter = 0;
|
|
|
|
var region = data['regions'][r_idx]
|
|
|
|
for (const r_idx in data['regions']) {
|
|
|
|
regions.push({
|
|
|
|
const region = data['regions'][r_idx]
|
|
|
|
id: r_idx,
|
|
|
|
this.regions.push({
|
|
|
|
name: region.name,
|
|
|
|
id: r_idx,
|
|
|
|
icon: 'airplane'
|
|
|
|
name: region.name,
|
|
|
|
});
|
|
|
|
icon: 'airplane'
|
|
|
|
for (var p_idx in region.points) {
|
|
|
|
|
|
|
|
var point = region.points[p_idx];
|
|
|
|
|
|
|
|
var loc = point.gps.split(',');
|
|
|
|
|
|
|
|
var lat = parseFloat(loc[0].trim())
|
|
|
|
|
|
|
|
var lon = parseFloat(loc[1].trim())
|
|
|
|
|
|
|
|
markersData.push({
|
|
|
|
|
|
|
|
location: [lat, lon],
|
|
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
|
|
text: point.name,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const p_idx in region.points) {
|
|
|
|
|
|
|
|
const point = region.points[p_idx];
|
|
|
|
|
|
|
|
const loc = point.gps.split(',');
|
|
|
|
|
|
|
|
const lat = parseFloat(loc[0].trim())
|
|
|
|
|
|
|
|
const lon = parseFloat(loc[1].trim())
|
|
|
|
|
|
|
|
this.markersData.push({
|
|
|
|
|
|
|
|
location: [lat, lon],
|
|
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
|
|
text: point.name,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
this.points.push({
|
|
|
|
|
|
|
|
id: point_counter,
|
|
|
|
|
|
|
|
location: [lat, lon],
|
|
|
|
|
|
|
|
text: point.name,
|
|
|
|
|
|
|
|
description: point.description,
|
|
|
|
|
|
|
|
regionId: r_idx,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
point_counter++;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Загрузка точек на карте из региона
|
|
|
|
// Получить маркеры для карты
|
|
|
|
function loadPoints(region) {
|
|
|
|
getMarkers() {
|
|
|
|
points = [];
|
|
|
|
return this.markersData;
|
|
|
|
for (var p_idx in region.points) {
|
|
|
|
}
|
|
|
|
var point = region.points[p_idx];
|
|
|
|
|
|
|
|
var loc = point.gps.split(',');
|
|
|
|
// Получить список регионов
|
|
|
|
var lat = parseFloat(loc[0].trim())
|
|
|
|
getRegions() {
|
|
|
|
var lon = parseFloat(loc[1].trim())
|
|
|
|
return this.regions;
|
|
|
|
points.push({
|
|
|
|
}
|
|
|
|
id: p_idx,
|
|
|
|
|
|
|
|
text: point.name,
|
|
|
|
// Получить точки которые относятся к региону
|
|
|
|
description: point.description,
|
|
|
|
getRegionPoints(r_idx) {
|
|
|
|
location: [lat, lon],
|
|
|
|
return this.points.filter(p => p.regionId == r_idx);
|
|
|
|
tooltip: {
|
|
|
|
}
|
|
|
|
text: point.description,
|
|
|
|
|
|
|
|
|
|
|
|
// Получить точки по переданным индексам точек
|
|
|
|
|
|
|
|
getSelectedPoints(indexes) {
|
|
|
|
|
|
|
|
let result = [];
|
|
|
|
|
|
|
|
this.points.forEach((point) => {
|
|
|
|
|
|
|
|
if (indexes.includes(point.id)) {
|
|
|
|
|
|
|
|
result.push(point);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Сортировка результата в порядке переданных индексов
|
|
|
|
|
|
|
|
let ordered = [];
|
|
|
|
|
|
|
|
indexes.forEach((ind)=>{
|
|
|
|
|
|
|
|
const index = result.findIndex(p=>p.id == ind);
|
|
|
|
|
|
|
|
ordered.push(result[index]);
|
|
|
|
|
|
|
|
result.slice(index, 1);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return ordered;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Точки для карты
|
|
|
|
|
|
|
|
const waypoints = new WaypointsData();
|
|
|
|
|
|
|
|
const markerUrl = './img/map-marker.png';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let regionPointsWidget;
|
|
|
|
|
|
|
|
let selectedPointsWidget;
|
|
|
|
|
|
|
|
|
|
|
|
// Инициализация контрола карты
|
|
|
|
// Инициализация контрола карты
|
|
|
|
function initMap() {
|
|
|
|
function initMap() {
|
|
|
|
const mapWidget = $('#map').dxMap({
|
|
|
|
const mapWidget = $('#map').dxMap({
|
|
|
@ -61,13 +96,13 @@ function initMap() {
|
|
|
|
width: 760,
|
|
|
|
width: 760,
|
|
|
|
controls: true,
|
|
|
|
controls: true,
|
|
|
|
markerIconSrc: markerUrl,
|
|
|
|
markerIconSrc: markerUrl,
|
|
|
|
markers: markersData,
|
|
|
|
markers: waypoints.getMarkers(),
|
|
|
|
type: 'roadmap'
|
|
|
|
type: 'roadmap'
|
|
|
|
}).dxMap('instance');
|
|
|
|
}).dxMap('instance');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Пересоздание списка точек указанного региона, для выбора мест к посещению
|
|
|
|
// Пересоздание списка точек указанного региона, для выбора мест к посещению
|
|
|
|
function rebuildRegionPointsList(region) {
|
|
|
|
function rebuildRegionPointsList(regionId) {
|
|
|
|
if (regionPointsWidget) {
|
|
|
|
if (regionPointsWidget) {
|
|
|
|
regionPointsWidget.dispose();
|
|
|
|
regionPointsWidget.dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -78,7 +113,7 @@ function rebuildRegionPointsList(region) {
|
|
|
|
dataSource: new DevExpress.data.DataSource({
|
|
|
|
dataSource: new DevExpress.data.DataSource({
|
|
|
|
store: new DevExpress.data.ArrayStore({
|
|
|
|
store: new DevExpress.data.ArrayStore({
|
|
|
|
key: 'id',
|
|
|
|
key: 'id',
|
|
|
|
data: points,
|
|
|
|
data: waypoints.getRegionPoints(regionId),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
height: 400,
|
|
|
|
height: 400,
|
|
|
@ -109,23 +144,16 @@ function initRegionDropDown() {
|
|
|
|
displayExpr: 'name',
|
|
|
|
displayExpr: 'name',
|
|
|
|
keyExpr: 'id',
|
|
|
|
keyExpr: 'id',
|
|
|
|
onItemClick(e) {
|
|
|
|
onItemClick(e) {
|
|
|
|
var region = data['regions'][e.itemData.id];
|
|
|
|
rebuildRegionPointsList(e.itemData.id);
|
|
|
|
loadPoints(region);
|
|
|
|
|
|
|
|
rebuildRegionPointsList(region);
|
|
|
|
|
|
|
|
DevExpress.ui.notify(`${e.itemData.name}`, 'success', 600);
|
|
|
|
DevExpress.ui.notify(`${e.itemData.name}`, 'success', 600);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
items: regions,
|
|
|
|
items: waypoints.getRegions(),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Перестроение списка выбранных точек
|
|
|
|
// Перестроение списка выбранных точек
|
|
|
|
function rebuildSelectedPointsList(selectedPointsIndexes) {
|
|
|
|
function rebuildSelectedPointsList(selectedPointsIndexes) {
|
|
|
|
let selected = [];
|
|
|
|
const selected = waypoints.getSelectedPoints(selectedPointsIndexes);
|
|
|
|
for (const p_idx in points) {
|
|
|
|
|
|
|
|
if (p_idx in selectedPointsIndexes) {
|
|
|
|
|
|
|
|
selected.push(points[p_idx]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectedPointsWidget) {
|
|
|
|
if (selectedPointsWidget) {
|
|
|
|
selectedPointsWidget.dispose();
|
|
|
|
selectedPointsWidget.dispose();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -177,6 +205,45 @@ function initRecalculateWaypointsButton() {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Отображение маршрута
|
|
|
|
|
|
|
|
function showTravelPoints(indexes) {
|
|
|
|
|
|
|
|
const points = waypoints.getSelectedPoints(indexes);
|
|
|
|
|
|
|
|
let calculatedPoints = [];
|
|
|
|
|
|
|
|
let prevPoint = null;
|
|
|
|
|
|
|
|
let id = 0;
|
|
|
|
|
|
|
|
for (const idx in points) {
|
|
|
|
|
|
|
|
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 calculateDistance(lat1, lon1, lat2, lon2) {
|
|
|
|
function calculateDistance(lat1, lon1, lat2, lon2) {
|
|
|
|
const R = 6371e3; // metres
|
|
|
|
const R = 6371e3; // metres
|
|
|
@ -209,43 +276,18 @@ function shuffle(array) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showTravelPoints(indexes) {
|
|
|
|
// Факториал
|
|
|
|
let calculatedPoints = [];
|
|
|
|
function factorial(n) {
|
|
|
|
let prevPoint = null;
|
|
|
|
j = 1;
|
|
|
|
let id = 0;
|
|
|
|
for (i = 1; i <= n; i++) {
|
|
|
|
for (const i in indexes) {
|
|
|
|
j = j * i;
|
|
|
|
const idx = indexes[i];
|
|
|
|
}
|
|
|
|
const point = points[idx];
|
|
|
|
return j;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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({
|
|
|
|
function stochasticPathFind(array) {
|
|
|
|
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,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Вычисление оптимального маршрута по начальной и конечной точке
|
|
|
|
// Вычисление оптимального маршрута по начальной и конечной точке
|
|
|
@ -303,7 +345,8 @@ function recalculateWaypoints(waypoints) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$(() => {
|
|
|
|
$(() => {
|
|
|
|
initData();
|
|
|
|
waypoints.loadData();
|
|
|
|
|
|
|
|
|
|
|
|
initMap();
|
|
|
|
initMap();
|
|
|
|
initRegionDropDown();
|
|
|
|
initRegionDropDown();
|
|
|
|
initRecalculateWaypointsButton();
|
|
|
|
initRecalculateWaypointsButton();
|
|
|
|