Автоматизация службы доставки требует точного определения тарифных зон. Ошибка оператора при выборе зоны может привести к убыткам или конфликтам с клиентами. В этой статье мы подробно разберем, как реализовать механизм автоматического определения зоны доставки непосредственно в 1С, используя внешние картографические сервисы и математические алгоритмы.
Для решения задачи нам необходимо пройти три основных этапа. Во-первых, превратить текстовый адрес клиента в географические координаты (широту и долготу). Этот процесс называется геокодированием. Во-вторых, нам нужно создать сами зоны (нарисовать их на карте) и загрузить их границы в информационную базу 1С. В-третьих, необходимо реализовать алгоритм, который определит, в какой из загруженных многоугольников (полигонов) попадает точка с координатами клиента.
Проанализируем каждый этап подробнее и разберем инструменты, которые нам понадобятся.
Прежде чем определять зону, нужно понять, где именно находится адрес. Для этого используются API крупных картографических сервисов. Рассмотрим основные варианты:
Выясним причину, по которой стоит выбрать именно программное получение координат: это исключает человеческий фактор. При получении координат мы сохраняем их в карточке контрагента или документа, чтобы не выполнять платные запросы повторно.
Самый простой способ отрисовать зоны — воспользоваться Конструктором карт Яндекса. Вы можете вручную нарисовать многоугольники, соответствующие вашим тарифам (например, "Центр", "МКАД", "Загород"). После того как карта готова, ее данные можно экспортировать. Однако для автоматизации лучше загружать их программно.
Рассмотрим пример кода, который позволяет получить данные о полигонах из конструктора карт. Для этого нам понадобится идентификатор карты (sid). Разберем по шагам функцию загрузки:
Функция ЗагрузитьГраницыЗон(ИдентификаторКарты)
Сервер = "api-maps.yandex.ru";
Соединение = Новый HTTPСоединение(Сервер, 443,,,,, Новый ЗащищенноеСоединениеOpenSSL());
ИмяФайла = ПолучитьИмяВременногоФайла();
// Запрос к сервису конструктора для получения JS-файла с данными
Запрос = Новый HTTPЗапрос("services/constructor/1.0/js/?sid=" + ИдентификаторКарты);
Ответ = Соединение.Получить(Запрос, ИмяФайла);
ТекстовыйФайл = Новый ТекстовыйДокумент;
ТекстовыйФайл.Прочитать(ИмяФайла, КодировкаТекста.UTF8);
ПолныйТекст = ТекстовыйФайл.ПолучитьТекст();
// Извлекаем JSON-структуру из тела скрипта
НачалоJSON = Найти(ПолныйТекст, "{" + Символы.ПС + " ""maps"":");
// Здесь требуется функция парсинга, чтобы вырезать нужную часть строки
JSONСтрока = ВырезатьДанныеJSON(ПолныйТекст);
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(JSONСтрока);
ДанныеКарты = ПрочитатьJSON(Чтение);
Для Каждого Объект Из ДанныеКарты.geoObjects.features Цикл
Если Объект.geometry.type = "Polygon" Тогда
// Создаем новый элемент справочника "ЗоныДоставки"
// Сохраняем массив координат вершин полигона
КоординатыВершин = Объект.geometry.coordinates[0];
СохранитьЗону(Объект.properties.name, КоординатыВершин);
КонецЕсли;
КонецЦикла;
КонецФункции
Обратите внимание на то, что координаты в JSON приходят в виде массива массивов. Нам необходимо сохранить их в структуру, удобную для дальнейшего поиска — например, в подчиненный справочник или в регистр сведений.
Теперь, когда у нас есть координаты клиента и координаты вершин всех наших зон, мы должны реализовать математическую проверку. Самый эффективный алгоритм для этого — Ray Casting (метод испускания луча). Суть проста: мы проводим горизонтальный луч из нашей точки и считаем, сколько раз он пересекает границы многоугольника. Если количество пересечений нечетное — точка внутри.
Рассмотрим реализацию этого алгоритма на языке 1С. Проанализируем функцию, которая принимает координаты точки и массив координат вершин зоны:
Функция ТочкаВнутриПолигона(ШиротаТочки, ДолготаТочки, МассивВершин) Экспорт
Результат = Ложь;
КоличествоВершин = МассивВершин.Количество();
j = КоличествоВершин - 1;
Для i = 0 По КоличествоВершин - 1 Цикл
// МассивВершин[i] содержит [Долгота, Широта]
ШиротаI = МассивВершин[i][1];
ДолготаI = МассивВершин[i][0];
ШиротаJ = МассивВершин[j][1];
ДолготаJ = МассивВершин[j][0];
Если ((ШиротаI > ШиротаТочки) <> (ШиротаJ > ШиротаТочки)) И
(ДолготаТочки < (ДолготаJ - ДолготаI) * (ШиротаТочки - ШиротаI) / (ШиротаJ - ШиротаI) + ДолготаI) Тогда
Результат = НЕ Результат;
КонецЕсли;
j = i;
КонецЦикла;
Возврат Результат;
КонецФункции
Этот код работает быстро и позволяет проверять вхождение в зоны любой сложности (выпуклые, вогнутые, с самопересечениями). Мы просто перебираем в цикле все наши зоны и вызываем данную функцию. Как только функция вернула Истина — зона найдена.
Если у вас сотни зон и тысячи заказов в час, простой перебор всех зон в коде 1С может замедлить работу. Посмотрим на способы оптимизации:
Если Широта > МинШирота И Широта < МаксШирота.... Это мгновенно отсечет 90% неподходящих зон.PostgreSQL с расширением PostGIS умеет выполнять такие расчеты на уровне SQL-запроса. 1С отправляет координаты, а база возвращает имя зоны.Реализация определения зоны доставки в 1С — это задача, требующая совмещения работы с внешними API и базовых знаний геометрии. Мы рассмотрели, как получить координаты через геокодер, как загрузить структуру JSON из конструктора карт и как применить алгоритм Ray Casting для финального расчета. Используя эти инструменты, вы сможете полностью автоматизировать расчет стоимости доставки, минимизировав ошибки персонала.