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

04.03.24

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

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

Скачать исходный код

Наименование Файл Версия Размер
Условно-бесплатное определение расстояний между точками с помощью сервисов геокодирования и Open source проектов:
.epf 9,48Kb
3
.epf 9,48Kb 3 Скачать

Как-то, около 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

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

См. также

Интеграция Альфа Авто 5 / Альфа Авто 6 и AUTOCRM / Инфотек

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

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

36000 руб.

03.08.2020    15993    13    18    

13

Интеграция 1С — Битрикс24. Обмен задачами

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс24. Разработка имеет двухстороннюю синхронизацию 1С и Битрикс24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (8.3.18.1289). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    18003    9    15    

14

Модуль для обмена "1С:Предприятие 8. УАТ. ПРОФ" с FortMonitor

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    12935    32    8    

11

SALE! 10%

Автоматическая загрузка файлов (например, прайс-листов) из электронной почты, FTP, HTTP, их обработка и выгрузка на FTP (на сайт) и для других целей

Прайсы WEB-интеграция Ценообразование, анализ цен Файловый обмен (TXT, XML, DBF), FTP Автомобили, автосервисы Оптовая торговля, дистрибуция, логистика Управленческий учет Платные (руб)

Программа с заданным интервалом времени (или по ручной команде) скачивает файлы (например, прайс-листы поставщиков) из различных источников: письма электронной почты, FTP или HTTP-адреса, и сохраняет их в каталог упорядоченной структуры. При этом извлекает файлы из архивов, может переименовывать файлы и менять их формат (csv, xls, txt). Можно настроить выгрузку обработанных файлов на сайт (через FTP-подключение). Программа будет полезна компаниям, у которых есть большое количество поставщиков и/или прайс-листы поставщиков обновляются часто (необязательно прайс-листы, файлы могут быть любого назначения). Собранные таким образом актуальные версии прайс-листов можно выгрузить с помощью программы себе на сайт (или на любой FTP-сервер) или выполнить другие необходимые задачи.

28000 25200 руб.

28.05.2015    85037    26    51    

50

Заполнение по ИНН или наименованию реквизитов контрагента по данным сайта ФНС

Обмен с ГосИС 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

2400 руб.

28.04.2016    89198    163    216    

320
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. rhtr 90 05.03.24 15:49 Сейчас в теме
в километрах, в метрах видимо.
2. Luis-Gomer 52 05.03.24 16:30 Сейчас в теме
3. grumagargler 724 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;
}
Показать
Prometeus2011; Luis-Gomer; +2 Ответить
4. Luis-Gomer 52 06.03.24 05:30 Сейчас в теме
5. swenzik 06.03.24 09:11 Сейчас в теме
(3) это по прямой, а их сервис считает по дорогам общего пользования. судя по структуре ответа они ретранслируют OSRM, в общем ничего не мешает установить его самостоятельно в виртуалке. работает неплохо, я применял его для планирования доставки заказов по курьерам.
Luis-Gomer; +1 Ответить
6. Prometeus2011 211 10.04.24 11:35 Сейчас в теме
Пару лет назад у них 'сохранять куда-нибудь туда' считалось нарушением условий бесплатного использования сервиса)
Оставьте свое сообщение