Условно-бесплатное определение расстояний между точками с помощью сервисов геокодирования и Open source проектов

04.03.24

Интеграция - WEB-интеграция

Добрый день. Сегодня расскажу о своих инженерных изысканиях в области карт/расстояний между точками и адресами.

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
Условно-бесплатное определение расстояний между точками с помощью сервисов геокодирования и Open source проектов:
.epf 9,48Kb
8 1 850 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Как-то, около 2х лет назад передо мной одна небольшая, но амбициозная фирма поставила задачу:

"Нам нужно определять расстояние от точки до точки в путевом листе Бухгалтерии автоматически, но, чтобы это стоило "мало деняг". 

Естественно, как инженеру, мне было интересно решить данную задачу и посмотреть, а есть ли вообще способы бесплатно или почти бесплатно решить поставленную задачу. Спойлер: решение есть.

API Яндекса и 2gis не рассматривались изначально, так как они платные и, если не ошибаюсь, только API 2Gis в тех годах стоял около 130 к в год . Может сейчас все изменилось. Ну, в общем, они не бесплатные.

Порыскав в поисках решения задачи, я нашел отличный русскоязычный сервис, который позволяет работать с адресами, обрабатывая данные по API. К моему сожалению, он не мог определять расстояние, но мог преобразовать адреса в координаты, а это уже что-то. Сам процесс преобразования адреса в координаты называется "геокодирование".

Название сервиса - "DaData". Чтобы не сочли за рекламу, я не буду рассказывать, как зарегаться на этом сервисе и поучить ключи. Загуглите, там дело 5 минут. Так вот, запрос для определения координат по адресу там стоит 15 копеек, а первые 100 запросов вообще бесплатно. Если еще и сохранять координаты адресов куда-нибудь в накопитель для повторного использования, то можно сэкономить еще больше. 

Ну, так как мы с вами инженеры - без кода никуда. Самое приятное в этом сервисе на мой взгляд - то, что предварительно не нужно как-то приводить к единому формату адреса. Сервис сам это делает где-то внутри, и очень хорошо работает с российскими адресами. 

Рассматривать пример определения расстояния будем на следующих адресах, которые случайно пришли мне в голову:

 - Москва Большая садовая 10
 - Москва Новослободская 35 (в обработке специально сделал ошибку, чтобы показать, что адрес принимается почти любой)

 

Именно в таком формате: без точек, запятых и так далее. 

Так, на сервисе мы зарегались и ключики получили. 
 

#Область ВспомогательныеФункции

//Инициализирует HTTP соединение
Функция СоединениеССервисом(АдресСервиса)
				
	SSL = Новый ЗащищенноеСоединениеOpenSSL(); 
	
	Попытка
		Соединение = Новый HTTPСоединение(АдресСервиса,,,,,,SSL);
	Исключение
		ЗаписьЖурналаРегистрации(СтрШаблон("Ошибка установки соединения с сервисом: %1", АдресСервиса), 
						УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
								
		Соединение = Неопределено;
		
	КонецПопытки;
	
	Возврат Соединение;
	
КонецФункции

// Функция - Запрос на сервис
//
// Параметры:
//  Соединение	 - HTTPСоединение - Соединение, установленное с сервисом см.СоединениеССервисом 
//  ЗапросHTTP	 - HTTPЗапрос - HTTPЗапрос с задаными (необходимыми) заголовками
//  Параметры	 - Строка - Строка в формате JSON
//  Метод		 - Строка	 - "GET" или "POST"
// 
// Возвращаемое значение:
//   - HTTPОтвет
//
Функция ЗапросНаСервис(Соединение, ЗапросHTTP, Параметры, Метод)
	
	Если Параметры <> Неопределено Тогда
		ЗапросHTTP.УстановитьТелоИзСтроки(Параметры);
	КонецЕсли;
	
	Попытка
		Если Метод = "GET" Тогда
			Ответ = Соединение.Получить(ЗапросHTTP);
		Иначе
			Ответ = Соединение.ОтправитьДляОбработки(ЗапросHTTP);
		КонецЕсли;
	Исключение
		
		ТекстОшибки = ОписаниеОшибки();
		ЗаписьЖурналаРегистрации("Отправка запроса HTTP завершилась с ошибкой", УровеньЖурналаРегистрации.Ошибка,,,ТекстОшибки);
				
		Ответ = Неопределено;
		
	КонецПопытки;
	
	Возврат Ответ;
			
КонецФункции

#КонецОбласти ВспомогательныеФункции

// Определяет координаты точки А и точки Б с помощью сервисов Dadata. Адрес необходимо писать в читаемой форме
// Например: "Москва Большая садовая 10", "Большая садовая 10 Москва" и т.п. Российские адреса воспринимает корректно
// В большинстве случаев. 
//
// Параметры:
//  ТочкаА	 - Строка	 - Адрес откуда считаем расстояние 
//  ТочкаБ	 - Строка	 - Адрес докуда считаем расстояние
// 
// Возвращаемое значение:
//   Структура: 
//		*КоординатыТочкиА - Структура - Координаты с ключами "Долгота" и "Широта"
//		*КоординатыТочкиБ - Структура - Координаты с ключами "Долгота" и "Широта"
//
Функция КоординатыССервисаDaData(ТочкаА, ТочкаБ) Экспорт
	
	КоординатыТочек  = Новый Структура("КоординатыТочкиА, КоординатыТочкиБ", "", "");
	
	СоединениеСDadata = СоединениеССервисом("cleaner.dadata.ru");
	
	//Обработаываем как нужно
	Если СоединениеСDadata = Неопределено Тогда
		Возврат КоординатыТочек;
	КонецЕсли;
		
	КоординатыТочкиА = КоординатыТочки(ТочкаА, СоединениеСDadata);	
	КоординатыТочкиБ = КоординатыТочки(ТочкаБ, СоединениеСDadata);
	
    КоординатыТочек.КоординатыТочкиА = КоординатыТочкиА; 
	КоординатыТочек.КоординатыТочкиБ = КоординатыТочкиБ;
	
	Возврат КоординатыТочек;
		
КонецФункции

//Получает координаты Долгота и Широта для одного адреса. (вспомогательная)
//
// Параметры:
//  Адрес				 - Строка - Адрес, у которого необходмо получить координаты
//  СоединениеСDadata	 - HTTPСоединение - Соединение, установленное с сервисом Dadata 
// 
// Возвращаемое значение:
//   - Структура:
//		*Долгота - Строка - Долгота
//		*Широта - Строка - Широта
//
Функция КоординатыТочки(Адрес, СоединениеСDadata)
	
	КоординатыТочки = Новый Структура("Долгота, Широта", "", "");
	
	ВашТокен = "";
	СекретныйКлюч = "";
	
	ЗапросHTTP = Новый HTTPЗапрос();
	ЗапросHTTP.АдресРесурса = "api/v1/clean/address";
	ЗапросHTTP.Заголовки.Вставить("Content-Type" , "application/json");
	ЗапросHTTP.Заголовки.Вставить("Authorization", "Token " + ВашТокен);
	ЗапросHTTP.Заголовки.Вставить("X-Secret"     , СекретныйКлюч);
	
	ПараметрыЗапроса = СтрШаблон("[ ""%1"" ]", Адрес);
	
	РезультатЗапроса = ЗапросНаСервис(СоединениеСDadata, ЗапросHTTP, ПараметрыЗапроса, "POST");
	
	//Обработайте ошибку, как вам нужно, если код состояния не 200
	Если РезультатЗапроса.КодСостояния <> 200 Тогда
		ЗаписьЖурналаРегистрации("Ошибка запроса, код состояния: " + РезультатЗапроса.КодСостояния, УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());				
	КонецЕсли;
	
	ЧтениеJSON = Новый ЧтениеJSON(); 
	
	МассивОтвета = Новый Массив;
	
	Попытка
		ЧтениеJSON.УстановитьСтроку(РезультатЗапроса.ПолучитьТелоКакСтроку());
		МассивОтвета = ПрочитатьJSON(ЧтениеJSON);
	Исключение		
		ЗаписьЖурналаРегистрации("Не удалось прочитать JSON по причине", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());				
	КонецПопытки;
		
	Если МассивОтвета.Количество() = 1 Тогда
		КоординатыТочки.Долгота = МассивОтвета[0].geo_lon; 
		КоординатыТочки.Широта = МассивОтвета[0].geo_lat;
	КонецЕсли;
	
	Возврат КоординатыТочки;
	
КонецФункции




На выхлопе мы с вами получаем координаты точек:

 

 

Все, половина дела сделана. Можно расслабиться (нет).

Теперь нам нужно куда-то зарядить эти координаты, чтобы получить расстояние. И тут, где-то на форумах я наткнулся на информацию о некоем открытом проекте - "OSRM", расшифровывается как "Open source routing machine". И вот он как раз делает то, что нам нужно. Сам ресурс находится по адресу: https://project-osrm.org/

У него достаточно полная документация. Не буду вдаваться в детали и подробности, можно посмотреть самим. Здесь я рассказываю конкретно о том, как решалась данная задача.

С этого сервиса, нам нужен метод: route. Метод вызывается "GET" запросом и в строку мы передаем - как координаты двух точек, так и с помощью чего мы планируем двигаться между ними: car , bike or foot. Как можно понять, данный сервис считает расстояние не по прямой, а именно так нам и нужно. 

Пришло время закодить эту историю:
 

// Отправляет запрос на определение расстояния между координатами
//
// Параметры:
//  КоординатыТочек	 - Структура:  
//		* КоординатыТочкиА - Структура - Координаты с ключами "Долгота" и "Широта"
//		* КоординатыТочкиБ - Структура - Координаты с ключами "Долгота" и "Широта
// 
// Возвращаемое значение:
//   - Структура:
//		*Расстояние - Число
//
Функция РасстояниеМеждуКоординатами(КоординатыТочек) Экспорт
	
	РасстояниеМеждуТочками = Новый Структура("Расстояние", 0);
	
    Соединение = СоединениеССервисом("router.project-osrm.org");
	
	ЗапросHTTP = Новый HTTPЗапрос();
	
	//Здесь прописываем строку с параметрами для get запроса.
	//https://project-osrm.org/docs/v5.24.0/api/ Тут можно посмотреть документацию. Расстояние можно строить, используя различные способы
	//передвижения "car , bike or foot". В данном случае вычислим для машины	
	ЗапросHTTP.АдресРесурса = СтрШаблон("route/v1/car/%1,%2;%3,%4", 
											КоординатыТочек.КоординатыТочкиА.Долгота,
												КоординатыТочек.КоординатыТочкиА.Широта,
													КоординатыТочек.КоординатыТочкиБ.Долгота,
														КоординатыТочек.КоординатыТочкиБ.Широта);
														
    РезультатЗапроса = ЗапросНаСервис(Соединение, ЗапросHTTP, Неопределено, "GET");
	
	//Здесь можно обработать код состояния
	Если РезультатЗапроса.КодСостояния <> 200 Тогда
		//Сделай что-то
	КонецЕсли;
		
	ЧтениеJSON = Новый ЧтениеJSON(); 	
	СтруктураОтвета = Новый Структура;
	
	Попытка
		ЧтениеJSON.УстановитьСтроку(РезультатЗапроса.ПолучитьТелоКакСтроку());
		СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
	Исключение		
		ЗаписьЖурналаРегистрации("Не удалось прочитать JSON по причине", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());				
	КонецПопытки;
					
	Если СтруктураОтвета.Свойство("routes") Тогда
		РасстояниеМеждуТочками.Расстояние = СтруктураОтвета.routes[0].distance;
	КонецЕсли;
	
	Возврат РасстояниеМеждуТочками;
	
КонецФункции

Никаких ключей, регистраций и т.п. Единственное с чем столкнулись: сервис иногда не совсем, как бы это сказать, доступен. И ничего с этим не поделаешь, придется ждать. Но, вроде как по документации, если правильно помню, можно развернуть эту красоту локально у себя.

Ну и в завершение поделюсь функцией, которая объединяет все вышенаписанное воедино:
 

#Область Вспомогательное

//Сопоставляет массив входящих условий оператором И. 
Функция Конъюкция(Операнды)	
	Для Каждого Операнд Из Операнды Цикл
		Если Операнд <> Истина Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	Возврат Истина;
КонецФункции

#КонецОбласти


// Получает расстояние от точки до точки, через сервисы определения координат и геокодирования
//
// Параметры:
//  ТочкаА	 - Строка	 - Адрес отправления
//  ТочкаБ	 - Строка	 - Адрес назначения
// 
// Возвращаемое значение:
//   - Число - Расстояние между точками
//
Функция РасстояниеОтТочкиАДоТочкиБ(ТочкаА, ТочкаБ) Экспорт
	
	РезультатОбработки = Новый Структура("ЕстьОшибки, ОписаниеОшибки, РезультатЗапроса", Ложь, "", "");
	
	//Сначала получим координаты с сервиса Dadata
	КоординатыТочек = КоординатыССервисаDaData(ТочкаА, ТочкаБ);
	
	Операнды = Новый Массив;
	
	Операнды.Добавить(ЗначениеЗаполнено(КоординатыТочек.КоординатыТочкиА.Долгота));
	Операнды.Добавить(ЗначениеЗаполнено(КоординатыТочек.КоординатыТочкиА.Широта));
	Операнды.Добавить(ЗначениеЗаполнено(КоординатыТочек.КоординатыТочкиБ.Долгота));
	Операнды.Добавить(ЗначениеЗаполнено(КоординатыТочек.КоординатыТочкиБ.Широта));
	
	ЗаполненыКоординаты = Конъюкция(Операнды);
	
	//Для примера, тупо проверка на общую заполненость. Можно реализовать более детальную проверку
	Если Не ЗаполненыКоординаты Тогда
		РезультатОбработки.ЕстьОшибки = Истина;
		РезультатОбработки.ОписаниеОшибки = "Не заполнены обязательные координаты";
		
		Возврат РезультатОбработки;		
	КонецЕсли;
	
	//Теперь получаем расстояние с опенсорсного проекта http://router.project-osrm.org/
	РасстояниеМеждуТочек = РасстояниеМеждуКоординатами(КоординатыТочек);
	
	РезультатОбработки.РезультатЗапроса = РасстояниеМеждуТочек.Расстояние;
	
	Возврат РезультатОбработки;
	
КонецФункции

На выхлопе мы получаем структуру со множеством данных об адресах, а также то, что нам и было нужно - расстояние в метрах.
 

 

Если сравнивать с Яндексом, то, можно сказать, что почти "пуля в пулю". 

 

 

На этом все. Не претендую на гениальность, но вдруг кому-нибудь будут полезны эти изыскания.

Обработку также прикладываю.

Проверено на следующих конфигурациях и релизах:

  • Бухгалтерия предприятия, редакция 3.0, релизы 3.1.22.86

Вступайте в нашу телеграмм-группу Инфостарт

Обработка расстояние геокодирование определение расстояния апи

См. также

Оптовая торговля Розничная торговля WEB-интеграция 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 Платные (руб)

Онлайн-заказ - это решение для автоматизации процесса оформления заказов на сайте в торговых организациях. Продукт обеспечивает легкое взаимодействие между компанией и клиентами через веб-интерфейс, интегрированный с 1С:Предприятие. Система позволяет снизить операционные расходы, повысить лояльность клиентов и оптимизировать работу отдела продаж.

57600 руб.

26.11.2024    6210    4    3    

7

WEB-интеграция Программист Бизнес-аналитик 1С v8.3 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Розница 3.0 Оптовая торговля, дистрибуция, логистика ИТ-компания Платные (руб)

Модуль "Экспортер" — это расширение для 1С, предназначенное для автоматизации процессов выгрузки данных. Оно позволяет эффективно извлекать, преобразовывать и передавать данные из систем 1С в интеграционную платформу Spot2D. Подсистема упрощает настройку, снижает количество ручных операций и обеспечивает удобный контроль данных.

14400 руб.

20.12.2024    3526    17    2    

19

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь 1С v8.3 1C:Бухгалтерия 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    21331    29    24    

24

WEB-интеграция 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Оптовая торговля, дистрибуция, логистика Россия Платные (руб)

В расширении реализован механизм интеграции между системой поставщика и Личным кабинетом СДТ. Реализован обмен заказами и реализациями (накладными), предусмотрено отслеживание статусов документов. Расширение предназначено для 1С:УТ 11.4.

35856 руб.

27.11.2024    1903    1    0    

1

Обмен с ГосИС WEB-интеграция Бухгалтер Пользователь 1С v8.3 Управляемые формы 1С:Комплексная автоматизация 1.х 1С:Бухгалтерия 2.0 1С:Управление торговлей 10 1С:Управление производственным предприятием 1С:Управление нашей фирмой 1.6 1С:Бухгалтерия государственного учреждения 1С:Документооборот 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Платные (руб)

Обработка является альтернативой механизму, разработанному фирмой 1С и заполняющему реквизиты контрагента по ИНН или наименованию. Не требуется действующей подписки ИТС. Вызывается как внешняя дополнительная обработка, т.е. используется, непосредственно, из карточки контрагента. Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС (egrul.nalog.ru) для БП 2.0, БП 3.0, БГУ 1.0, БГУ 2.0, УТ 10.3, УТ 11.x, КА 1.1, КА 2.x, УПП 1.x, ERP 2.x, УНФ 1.5, УНФ 1.6, УНФ 3.0, ДО 2.1

5196 руб.

28.04.2016    97517    110    218    

359

WEB-интеграция Программист 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Бытовые услуги, сервис Платные (руб)

Внешняя обработка разработана для автоматизации передачи данных между сервисом Vetmanager с 1С: Бухгалтерия 3.0. Решение позволяет загружать документы и справочники из Ветменеджер в 1С:Бухгалтерию, сокращая время на ручной ввод данных и минимизируя ошибки.

12000 руб.

02.02.2021    20353    58    52    

36
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. rhtr 94 05.03.24 15:49 Сейчас в теме
в километрах, в метрах видимо.
2. Luis-Gomer 59 05.03.24 16:30 Сейчас в теме
3. grumagargler 731 06.03.24 02:03 Сейчас в теме
если кому надо, вот набольшая формула на плюсах для расчёта расстояния между двумя точками по прямой, может пригодиться для задач нахождения объектов в заданном радиусе:

double distance ( double latitude1, double longitude1, double latitude2, double longitude2 ) {
	double dLat = ( latitude2 - latitude1 ) * M_PI / 180.0;
	double dLon = ( longitude2 - longitude1 ) * M_PI / 180.0;
	latitude1 = latitude1 * M_PI / 180.0;
	latitude2 = latitude2 * M_PI / 180.0;
	double a = pow ( sin ( dLat / 2 ), 2 ) +
		pow ( sin ( dLon / 2 ), 2 ) *
		cos ( latitude1 ) * cos ( latitude2 );
	double rad = 6371;
	double c = 2 * asin ( sqrt ( a ) );
	return rad * c;
}
Показать
aleksey2; Prometeus2011; Luis-Gomer; +3 Ответить
4. Luis-Gomer 59 06.03.24 05:30 Сейчас в теме
5. swenzik 06.03.24 09:11 Сейчас в теме
(3) это по прямой, а их сервис считает по дорогам общего пользования. судя по структуре ответа они ретранслируют OSRM, в общем ничего не мешает установить его самостоятельно в виртуалке. работает неплохо, я применял его для планирования доставки заказов по курьерам.
Luis-Gomer; +1 Ответить
6. Prometeus2011 178 10.04.24 11:35 Сейчас в теме
Пару лет назад у них 'сохранять куда-нибудь туда' считалось нарушением условий бесплатного использования сервиса)
7. MatriX 15.05.25 19:21 Сейчас в теме
почему -то, при расчете от МКАД показывает в 2.5 раза больше. Пока не понимаю, где ошибка.
8. Luis-Gomer 59 22.05.25 15:21 Сейчас в теме
(7) А не подскажете адрес "откуда"- "куда"? Постараюсь помочь.
1prog@bk.ru; +1 Ответить
Для отправки сообщения требуется регистрация/авторизация