В логистических задачах часто возникает необходимость определить границы зоны обслуживания. Одной из типичных задач является расчет "коридора" вдоль маршрута: например, если точка доставки находится в пределах 20 км от основной трассы, доставка считается бесплатной. В этой статье мы подробно разберем, как вычислить географические координаты (широту и долготу) на заданном расстоянии от точек пути и как правильно организовать проверку вхождения адреса в эту зону.
Предположим, у нас есть маршрут, состоящий из последовательности точек с известными координатами. Нам нужно построить вокруг этого маршрута буферную зону шириной 20 км. Для реализации этого в 1С существует три основных подхода, которые мы проанализируем по мере возрастания точности и сложности:
Рассмотрим самый простой способ, который предложили участники обсуждения. На небольших расстояниях (до 50-100 км) кривизной Земли можно пренебречь и считать, что 1 градус широты и долготы имеет фиксированную длину в километрах.
Важный нюанс: длина 1 градуса широты всегда составляет примерно 111.1 км. Однако длина 1 градуса долготы уменьшается при движении от экватора к полюсам. Проанализируем формулу расчета:
ДлинаГрадусаДолготы = 111.3 * Cos(ШиротаВРадианах)
Для реализации в 1С нам потребуется перевести широту из градусов в радианы. Разберем пример функции:
Функция ПолучитьСмещениеКоординат(ЦентральнаяТочка, РасстояниеКМ)
Пи = 3.14159265358979;
РадиусЗемли = 6371;
// Широта точки в радианах
ШиротаРад = ЦентральнаяТочка.Широта * Пи / 180;
// Длина одного градуса в данной точке
КмВГрадусеШироты = 111.1;
КмВГрадусеДолготы = 111.1 * Cos(ШиротаРад);
// Смещение в градусах
СмещениеШироты = РасстояниеКМ / КмВГрадусеШироты;
СмещениеДолготы = РасстояниеКМ / КмВГрадусеДолготы;
Результат = Новый Структура;
Результат.Вставить("Север", ЦентральнаяТочка.Широта + СмещениеШироты);
Результат.Вставить("Юг", ЦентральнаяТочка.Широта - СмещениеШироты);
Результат.Вставить("Восток", ЦентральнаяТочка.Долгота + СмещениеДолготы);
Результат.Вставить("Запад", ЦентральнаяТочка.Долгота - СмещениеДолготы);
Возврат Результат;
КонецФункции
Этот метод позволяет быстро получить "ограничивающий прямоугольник" (Bounding Box). Однако для построения точных точек по бокам маршрута этого недостаточно, так как нужно учитывать направление (азимут) движения транспорта.
Чтобы найти точку С на расстоянии 20 км строго перпендикулярно маршруту АВ, нам нужно сначала вычислить азимут (направление) от А к В, прибавить к нему 90 градусов и решить "прямую геодезическую задачу".
Рассмотрим формулу для сферы, которую можно внедрить в код 1С:
Широта2 = arcsin( sin(lat1) * cos(d/R) + cos(lat1) * sin(d/R) * cos(θ) )
Долгота2 = lon1 + atan2( sin(θ) * sin(d/R) * cos(lat1), cos(d/R) - sin(lat1) * sin(lat2) )
Где d — расстояние, R — радиус Земли, θ — азимут в радианах.
Разберем по шагам, как это реализовать. Сначала вычислим азимут между двумя точками маршрута:
Функция ВычислитьАзимут(Лат1, Лон1, Лат2, Лон2)
Пи = 3.14159265358979;
ф1 = Лат1 * Пи / 180;
ф2 = Лат2 * Пи / 180;
дЛ = (Лон2 - Лон1) * Пи / 180;
y = Sin(дЛ) * Cos(ф2);
x = Cos(ф1) * Sin(ф2) - Sin(ф1) * Cos(ф2) * Cos(дЛ);
Рез = Atan2(y, x);
Возврат Рез; // Азимут в радианах
КонецФункции
Теперь, зная направление сегмента дороги, мы можем вычислить координаты точек "справа" и "слева" от дороги, прибавив или отняв Пи/2 (90 градусов) к полученному азимуту.
Проанализируем ситуацию: пользователь предложил искать ближайшую точку (узел) маршрута и считать расстояние до нее. Это неверный подход для длинных перегонов. Если дорога прямая и длится 100 км, а точка доставки находится посередине в 5 км от дороги, то расстояние до ближайшего "узла" (начала или конца дороги) составит 50 км. Система ошибочно определит, что доставка платная.
Правильное решение — вычислять Cross-track distance (отклонение от пути). Это кратчайшее расстояние от точки (адреса клиента) до дуги большого круга (сегмента дороги).
Формула расчета в радианах:
dxt = arcsin( sin(d13/R) * sin(θ13 - θ12) ) * R
Где:
d13 — расстояние от начала сегмента до точки доставки;θ13 — азимут от начала сегмента на точку доставки;θ12 — азимут самого сегмента дороги.Выясним, как сделать работу системы максимально быстрой. Постоянный расчет тригонометрии для каждой точки маршрута при каждом заказе нагрузит сервер. Проанализируем способы оптимизации:
isPointInPolygon или расчет дистанции до ломаной линии).ST_DWithin(geometry, geometry, distance) моментально определит, находится ли точка клиента в пределах 20 км от всей линии маршрута, используя пространственные индексы.Turf.js, вы получите доступ к функции turf.buffer(), которая автоматически строит зону любой сложности вокруг маршрута.Резюме: Для простых задач в пределах одного города достаточно использовать упрощенный метод с расчетом длины градуса долготы через косинус широты (Метод 1). Для межрегиональных маршрутов (как в примере 350 км) необходимо использовать либо расчет отклонения от пути (Cross-track distance), либо специализированные ГИС-инструменты, чтобы избежать ошибок, связанных с кривизной земной поверхности.