Берем Leaflet API
1. Создаем текстовый общий макет:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<link rel="stylesheet" href="/redirect.php?url=aHR0cHM6Ly91bnBrZy5jb20vbGVhZmxldEAxLjMuMS9kaXN0L2xlYWZsZXQuY3Nz" />
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
</head>
<body>
<div id="map" class="map" style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px;"></div>
<script type="text/javascript">
var map = L.map('map');
map.setView([&ШиротаЦентр, &ДолготаЦентр], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© <a href="/redirect.php?url=aHR0cDovL29zbS5vcmcvY29weXJpZ2h0">OpenStreetMap</a> and © <a href="/redirect.php?url=aHR0cDovL215LnNlcnZlcg==">Тут наша организация</a> contributors'
}).addTo(map);
&КодМаршрута
</script>
</body>
</html>
Версия leaflet указывается актуальная, либо используйте ту, которая в примере.. В примере кода заменяются ссылки на неверные <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" />
В коде HTML присутствует 3 параметра: &ШиротаЦентр, &ДолготаЦентр, &КодМаршрута, которые нам нужно будет заменить на свои, при выводе текста в поле HTML документа. Первые два параметра указывают на центр карты для начального отображения (предлагается ставить координаты первой точки маршрута), третий - сам "код" (скрипт) маршрута (возможно сделать встроенные функции на JS, но мне было лень)).
2. Вторым шагом пишем две процедуры (в общем модуле, где хотите), первая - определение геокодирования адреса (получение координат по представлению адреса), описание сервиса доступно по ссылке Nominatim:
Функция ГеокодироватьАдрес(ТекАдрес) Экспорт
оср = Новый HTTPСоединение("nominatim.openstreetmap.org", , , , , , Новый ЗащищенноеСоединениеOpenSSL());
мАдрес = СтрРазделить(ТекАдрес, " ");
Попытка
Запрос = Новый HTTPЗапрос("/?format=json&q=" + СтрСоединить(мАдрес, "+"));
Ответ = оср.Получить(Запрос);
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка при попытке геокодировать по OSR, адрес: " + ТекАдрес + Символы.ПС + "Описание: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Попытка
СтруктАдреса = ПрочитатьJSON(ЧтениеJSON);
Исключение
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка при попытке геокодировать по OSR, адрес: " + ТекАдрес + Символы.ПС + "Описание: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Если ТипЗнч(СтруктАдреса) = Тип("Массив") Тогда
Если СтруктАдреса.Количество() = 0 Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Для адреса: """ + ТекАдрес + """ не обнаружено ни одной геопозиции");
Возврат Неопределено;
ИначеЕсли СтруктАдреса.Количество() > 1 Тогда
ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Для адреса: """ + ТекАдрес + """ обнаружено больше одной геопозиции");
КонецЕсли;
Адрес = СтруктАдреса[0];
Возврат Новый Структура("Адрес,Широта,Долгота", Адрес.display_name, Адрес.lat, Адрес.lon)
КонецЕсли;
КонецФункции
немного комментария к тексту кода: адрес указывать (область, город улица, номер дома) через пробелы. При геокодировании возможен возврат нескольких координат, в данном случае берется первый.
вторая - получение точек маршрута от routing machine
Функция ПолучитьМаршрут(МассивАдресов)
оср = Новый HTTPСоединение("router.project-osrm.org", , , , , , Новый ЗащищенноеСоединениеOpenSSL());
мТочки = Новый Массив;
Для каждого Точка Из МассивАдресов Цикл
мТочки.Добавить(Формат(Точка.Долгота, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0") + "," + Формат(Точка.Широта, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0"));
КонецЦикла;
Попытка
Запрос = Новый HTTPЗапрос("/route/v1/driving/" + СтрСоединить(мТочки, ";") + "?overview=false&alternatives=true&steps=true");
Ответ = оср.Получить(Запрос);
Исключение
Сообщить("Ошибка при попытке маршутизации по OSR, адрес" + Символы.ПС + "Описание: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Попытка
СтруктМаршрута = ПрочитатьJSON(ЧтениеJSON);
Исключение
Сообщить("Ошибка при попытке маршутизации по OSR, адрес:" + Символы.ПС + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Если СтруктМаршрута.Свойство("code") Тогда
Если НРег(СтруктМаршрута.code) = "ok" Тогда
Результат = Новый Структура("Маршрут,АльтМаршрут");
Если СтруктМаршрута.routes.Количество() Тогда
Результат.Маршрут = Новый Массив;
Для каждого Нога Из СтруктМаршрута.routes[0].legs Цикл
Для каждого ШагМаршрута Из Нога.steps Цикл
Для каждого Секция Из ШагМаршрута.intersections Цикл
Результат.Маршрут.Добавить(Новый Структура("Долгота,Широта", Секция.location[0], Секция.location[1]));
КонецЦикла;
КонецЦикла;
КонецЦикла;
Если СтруктМаршрута.routes.Количество() > 1 Тогда
Результат.АльтМаршрут = Новый Массив;
Для каждого Нога Из СтруктМаршрута.routes[0].legs Цикл
Для каждого ШагМаршрута Из Нога.steps Цикл
Для каждого Секция Из ШагМаршрута.intersections Цикл
Результат.АльтМаршрут.Добавить(Новый Структура("Долгота,Широта", Секция.location[0], Секция.location[1]));
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЕсли;
КонецЕсли;
Возврат Результат;
Иначе
Возврат Неопределено;
КонецЕсли;
ИначеЕсли СтруктМаршрута.Свойство("message") Тогда
Сообщить("Ошибка при попытке маршутизации по OSR, адрес:" + Символы.ПС + СтруктМаршрута.message);
Возврат Неопределено;
КонецЕсли;
КонецФункции // ПолучитьМаршрут()
МассивАдресов - передаем сюда наш массив, полученный в первой процедуре. Для более оптимальной работы, предлагаю хранить координаты в контактной информации объекта (напр. контрагента), получать их на стадии создания адреса. Да забыл, здесь мы получаем не только основной маршрут но и альтернативный (массив АльтМаршрут в структуре результата), его мы нарисуем другим цветом.
3. Теперь нам осталось только создать на основании наших точек сам JS код, который нарисует на полилинию на карте, и поставит точки начала и конца маршрута (ов).
Функция СформироватьJavaScriptМаршрута(МассивАдресов, Маршрут = Неопределено, АльтМаршрут = Неопределено) Экспорт
КодМаршрута = "";
Для каждого Адрес Из МассивАдресов Цикл
КодМаршрута = КодМаршрута + "L.marker(["+Формат(Адрес.Широта, "ЧЦ=16; ЧДЦ=12; ЧРД=.; ЧГ=0")+", "+Формат(Адрес.Долгота, "ЧЦ=16; ЧДЦ=12; ЧРД=.; ЧГ=0")+"], {title: '" + Адрес.Адрес + "'}).addTo(map);
| ";
КонецЦикла;
Если НЕ Маршрут = Неопределено Тогда
мМаршрут = Новый Массив;
Для каждого ТочкаМаршрута Из Маршрут Цикл
мМаршрут.Добавить("[" + Формат(ТочкаМаршрута.Широта, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0")+", "+Формат(ТочкаМаршрута.Долгота, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0") + "]");
КонецЦикла;
КодМаршрута = КодМаршрута + "var latlngs = [" + СтрСоединить(мМаршрут, ",") + "];
| var polyline = L.polyline(latlngs, {color: 'red', weight: 5, opacity: 0.7}).addTo(map);
| map.fitBounds(polyline.getBounds());
| ";
КонецЕсли;
Если НЕ АльтМаршрут = Неопределено Тогда
мМаршрут.Очистить();
Для каждого ТочкаМаршрута Из АльтМаршрут Цикл
мМаршрут.Добавить("[" + Формат(ТочкаМаршрута.Широта, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0")+", "+Формат(ТочкаМаршрута.Долгота, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0") + "]");
КонецЦикла;
КодМаршрута = КодМаршрута + "var altlatlngs = [" + СтрСоединить(мМаршрут, ",") + "];
| var altpolyline = L.polyline(altlatlngs, {color: 'blue', weight: 5, opacity: 0.7}).addTo(map);
| map.fitBounds(altpolyline.getBounds());
| ";
КонецЕсли;
Возврат КодМаршрута;
КонецФункции // СформироватьJavaScriptМаршрута()
Первым параметром передаем наш массив (с первой функции), вторым и третьим передаем массивы маршрутов со второй функции
Вот и все, теперь осталось подменить строки параметров в тексте HTML и засунуть его в наше поле карты.
Кому лень разбираться, предлагается тестовая обработка. Не судите строго ))
Тестовая обработка написана под УФ (тестировалось на платформе 8.3.11)
З.Ы. в построении маршрута обнаружился некий недочет - изгибы дорог и повороты (некоторые) почему то не учитываются, надо разбираться...
Обновлено 12.02.19 г.
Для более точной прорисовки изгибов и внутриквартальных поворотов, изменяем запрос к ресурсу routing machine:
"?overview=full&alternatives=true&steps=true&geometries=geojson&hints=;"
и немного дорабатываем процедуру обработки ответа:
Функция ПолучитьМаршрут(МассивАдресов)
оср = Новый HTTPСоединение("router.project-osrm.org", , , , , , Новый ЗащищенноеСоединениеOpenSSL());
мТочки = Новый Массив;
Для каждого Точка Из МассивАдресов Цикл
мТочки.Добавить(Формат(Точка.Долгота, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0") + "," + Формат(Точка.Широта, "ЧЦ=15; ЧДЦ=12; ЧРД=.; ЧГ=0"));
КонецЦикла;
Попытка
Запрос = Новый HTTPЗапрос("/route/v1/driving/" + СтрСоединить(мТочки, ";") + "?overview=full&alternatives=true&steps=true&geometries=geojson");
Ответ = оср.Получить(Запрос);
Исключение
Сообщить("Ошибка при попытке маршутизации по OSR, адрес" + Символы.ПС + "Описание: " + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(Ответ.ПолучитьТелоКакСтроку());
Попытка
СтруктМаршрута = ПрочитатьJSON(ЧтениеJSON);
Исключение
Сообщить("Ошибка при попытке маршутизации по OSR, адрес:" + Символы.ПС + ОписаниеОшибки());
Возврат Неопределено;
КонецПопытки;
Если СтруктМаршрута.Свойство("code") Тогда
Если НРег(СтруктМаршрута.code) = "ok" Тогда
Результат = Новый Структура("Маршрут,АльтМаршрут");
Если СтруктМаршрута.routes.Количество() Тогда
Результат.Маршрут = Новый Массив;
ОсновнойМаршрут = СтруктМаршрута.routes[0];
Если ОсновнойМаршрут.Свойство("geometry") И ОсновнойМаршрут.geometry.coordinates.Количество() Тогда
Для каждого Координаты Из ОсновнойМаршрут.geometry.coordinates Цикл
Результат.Маршрут.Добавить(Новый Структура("Долгота,Широта", Координаты[0], Координаты[1]));
КонецЦикла;
Иначе
Для каждого Нога Из ОсновнойМаршрут.legs Цикл
Для каждого ШагМаршрута Из Нога.steps Цикл
Для каждого Секция Из ШагМаршрута.intersections Цикл
Результат.Маршрут.Добавить(Новый Структура("Долгота,Широта", Секция.location[0], Секция.location[1]));
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЕсли;
Если СтруктМаршрута.routes.Количество() > 1 Тогда
Результат.АльтМаршрут = Новый Массив;
Альтернативный = СтруктМаршрута.routes[1];
Если Альтернативный.Свойство("geometry") И Альтернативный.geometry.coordinates.Количество() Тогда
Для каждого Координаты Из Альтернативный.geometry.coordinates Цикл
Результат.АльтМаршрут.Добавить(Новый Структура("Долгота,Широта", Координаты[0], Координаты[1]));
КонецЦикла;
Иначе
Для каждого Нога Из Альтернативный.legs Цикл
Для каждого ШагМаршрута Из Нога.steps Цикл
Для каждого Секция Из ШагМаршрута.intersections Цикл
Результат.АльтМаршрут.Добавить(Новый Структура("Долгота,Широта", Секция.location[0], Секция.location[1]));
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат Результат;
Иначе
Возврат Неопределено;
КонецЕсли;
ИначеЕсли СтруктМаршрута.Свойство("message") Тогда
Сообщить("Ошибка при попытке маршутизации по OSR, адрес:" + Символы.ПС + СтруктМаршрута.message);
Возврат Неопределено;
КонецЕсли;
КонецФункции // ПолучитьМаршрут()
Обновлена обработка...
Обновлено 01.06.2020
Убираем параметр "hints=" из строки параметров запросов к ресурсу расчета маршрута!