Задача простая. Нужно вычислить расстояние между двумя точками на карте, при этом известны только их географические координаты, то есть широта и долгота. Для примера вычислим расстояние между Москвой и Питером, но данная методика, конечно же, будет применима и к другим двум точкам местности. В наше время у многих людей есть такие компасы, например при смартфонах, которые показывают не только направление, но и географические координаты.
Итак, задача: вычислить расстояние между двумя городами. Известно только одно – географические координаты, то есть широта и долгота. Посмотрим эти данные в справочнике, ну или в Википедии.
Итак, что нам известно: координаты Москвы:
55,7522 град. с.ш., 37.6156 град. в.д.
Координаты Петербурга:
59,89444 град. с.ш., 30,26417 град. в.д.
Построим с помощью Excel трапецию:
Итак, у нас есть трапеция ABCD. На ней точка D – это Москва, точка B – это Петербург. Отрезок AB проходит по меридиану Петербурга, BC – по параллели Петербурга, CD – по меридиану Москвы и AD – по параллели Москвы.
Что нам известно? Во-первых известны все географические координаты каждой из точек:
A: 55,7522, 30,26417;
B: 59,89444, 30,26417;
C: 59,89444, 37,6156;
D: 55,7522, 37,6156.
Вычислить AB и CD достаточно просто. На меридианах в градусе примерно одинаковое число километров. Это расстояние можно взять из справочных данных, и оно составляет примерно 111,1 км в каждом градусе.
Нужная нам разница в градусах – это 59,89444-55,7522, или 4,14224. А это значит, что разница в километрах – это 4,14224*111,1=460,2029 км.
Что же касается оснований трапеции, там тоже все достаточно просто. Экватор – это ноль градусов северной широты, и длина каждого градуса на экваторе около 111,3 км. Поскольку косинус ноля – это единица, то для любой параллели верна следующая формула: длина каждого градуса равна произведению 111,3 на косинус того угла, который числится в градусах северной широты (ну или южной, если это происходит южнее экватора).
Итак, с помощью Excel вычислим нужные нам косинусы:
- cos(55.7522) = 0,562773
- cos(59.89444) = 0,501595
Это значит, что 1 градус в верхнем основании нашей трапеции =111,3*0,501595=55,82749 км, а один градус в нижнем основании трапеции =111,3*0,562773=62,6366 км. Поскольку число градусов одинаково как в верхнем, так и в нижнем основаниях трапеции и составляет 37,6156-30,26417, то есть 7,35143 градуса. Но число километров в верхнем и нижнем основаниях трапеции не одинаковое.
Рассчитаем эти расстояния в километрах. BC=7,35143*55,82749=410,4119 км.
AD=7,35143*62,6366=460,469 км.
Теперь проведем высоту BH в нашей трапеции:
В прямоугольном треугольнике ABH нам известно, что гипотенуза равна 460,2029 км, малый катет тоже известен (это половина разницы между длинами оснований трапеции, то есть 0,5*[460,469-410,4119], то есть 0,5*50,05711, или 25,02856 км).
Итак, найдем высоту трапеции ABCD, ее можно вычислить с помощью теоремы Пифагора. Напомню, что мы знаем и длину гипотенузы, и длину наименьшего из катетов.
- Квадрат гипотенузы: 211786,7
- Квадрат известного катета: 626,4
- Разность между этими числами: 211160,2
Корень из этой разности – 459,5218 км. Это и есть наша высота трапеции, то есть BH.
Задача почти решена. Для нахождения расстояния между Москвой и Питером нам нужно вычислить диагональ трапеции, то есть BD. Нарисуем эту линию:
Итак, у нас есть треугольник BHD. BH мы только что вычислили (459,5218 км), HD тоже известно (нужно от большего основания трапеции отнять AH. 460,469-25,02856=435,4404).
Два катета известны, нужно найти гипотенузу. По той же теореме Пифагора, и мы увидим, что гипотенуза будет равна 633,0629 км. Это и есть расстояние от Питера до Москвы.
Проверим наши вычисления, спросив у Яндекса, сколько составляет расстояние от Питера до Москвы.
Мы увидим ответ – 634 км. При вычислениях по нашей методике получилось чуть больше, чем 633 км. Это значит, что погрешность при данном виде вычислений достаточна мала. Но если учесть, что крупные города – это не маленькие точки, а большие расстояния с севера на юг и с запада на восток, то можно сказать, что мы вычислили все правильно.
А на этом пока всё, подписывайтесь на мой канал и до новых встреч!
Расстояние между двумя координатами
Онлайн калькулятор рассчитывает расстояние между двумя географическими координатами.
Инструкция по использованию калькулятора
Широту и долготу двух точек, между которыми необходимо найти расстояние, следует указывать в градусах в виде десятичной дроби.
Например, расстояние между Москвой (55.75059; 37.61777) и Киевом (50.44952; 30.52537) составляет 755 километров.
Южная широта и западная долгота задаются отрицательной величиной от 0° до -90° и от 0° до -180° соответственно.
Поделиться страницей в социальных сетях:
На чтение 5 мин. Просмотров 2.4k. Опубликовано 12.08.2022
Спутниковые координаты не только помогают найти маршрут до нужного места, но и позволяют рассчитать отрезок между двумя любыми точками в пространстве или на данной плоскости. Зная необходимые данные и координаты интересующих объектов, можно найти расстояние в километрах или метрах между этими длинами или отрезками нужной длины. Для этого применяется либо сложная формула для самостоятельного решения, либо онлайн-калькуляторы на сайте или картографические программы, выполняющие работу автоматически.
Содержание
- Координаты GPS
- Вычисление расстояния между двумя точками через формулу
- Формула для нахождения расстояния между точками
- Способы решения и нахождения расстояния между точками по координатам
- Онлайн-калькулятор для расчета расстояния между точками по координатам
- Картографическая программа для нахождения расстояния между точками
- Откуда берется погрешность при расчете расстояния между точками
Координаты GPS
Координаты GPS (Global Positioning System) – это цифровые обозначения местоположения устройства в пространстве или на плоскости, указанные в формате соотнесения географических широты и долготы. Данные точки на отрезке определенной длины вычисляются при помощи связи со спутником. Запускается сложная система навигации, которая, помимо указания координат, может определить расстояние между двумя точками в пространстве или на плоскости, проложить маршрут между отрезками длины и рассчитать время перемещения как пешком, так и на разных видах транспорта. В основе работы лежит всемирная система координат WGS 84.
Чтобы найти местоположение и расстояние между двумя данными точками или отрезками длины в пространстве и на плоскости по координатам GPS, можно пользоваться как специальным навигатором или профессиональным навигационным прибором, так и обычным смартфоном или планшетом.
Вычисление расстояния между двумя точками через формулу
Найти расстояние между двумя точками в пространстве или на плоскости можно как по прямой, так и по маршруту (с учетом расположения дорог, их поворотов, объездов и пр.). В первом случае применима специальная формула, воспользоваться которой можно как в автоматическом режиме, введя известные данные отрезков в калькулятор на сайте, так и самостоятельно, проведя итоговое решение с нужными материалами на бумаге.
Кратчайшим (прямым) расстоянием считается дуга, проходящая по поверхности Земли от точки А в точку Б. Чтобы найти ее длину, применяют так называемую модифицированную формулу гаверсинусов, учитывающую радиус планеты.
Известно, что Земля – не идеальный шар, а несколько приплюснутый, потому и радиус у нее в разных точках различен. Ввиду этого для подсчета кратчайшего расстояния между точками используется усредненное значение радиуса относительно оси (6372.795 км для Земли), что допускает погрешность итогового значения около 0,5 %.
Формула для нахождения расстояния между точками
В формуле, при помощи которой можно найти расстояния между двумя данными точками планеты с использованием координат, присутствуют следующие величины (известные из математики):
- d – центральный угол (перпендикуляр) между двумя данными точками, лежащими на большом круге (т. е. на окружности, получаемой при сечении центральной части шара плоскостью);
- r – радиус сферы (т. е. усредненное значение радиуса Земли: 6372.795 км);
- y₁ и y₂ – широта двух точек в радианах;
- x₁ и x₂ – долгота двух точек в радианах.
Получим следующую тригонометрическую формулу, плавно вытекающую из теоремы пифагора (евклидова геометрия), которая равна:
cos(d) = sin(y₁)·sin(y₂) + cos(y₁)·cos(y₂)·cos(x₁ − x₂)
Данное соотношение можно получить из прямоугольного треугольника.
Подставив в формулу заданные значения точек, и получим вычисление.
Для того чтобы найти ответ про расстояние между двумя точками координат в километрах, поможет формула:
L = d·R.
Способы решения и нахождения расстояния между точками по координатам
Чтобы провести решение и получить ответ о расстоянии точек в пространстве или плоскости по координатам GPS, необязательно использовать формулу вручную. Ответ о расстоянии между точками по координатам получим при помощи специальных утилит.
Онлайн-калькулятор для расчета расстояния между точками по координатам
В интернете есть множество сайтов с однотипными формулами в онлайн-калькуляторах для решения и нахождения прямого расстояния между двумя точками по координатам. Для этого нужно узнать широту и долготу двух искомых точек в пространстве или на плоскости и вбить эти данные в соответствующие окошки формулы (чем больше знаков после запятой у каждой точки известно, тем точнее получим значение).
Картографическая программа для нахождения расстояния между точками
Вычислять расстояние между двумя точками на плоскости по координатам и давать точный ответ умеет любое приложение-навигатор и без вычисления по формуле, например:
- «Карты»;
- «Google.Maps»;
- «Google Планета Земля»;
- «SAS.Планета».
Для определения расстояния между точками не по дорогам и маршрутам, а напрямую по двум точкам, применяется инструмент «Линейка».
Многие из популярных навигаторов способны определять расстояние по координатам двух точек онлайн без формул: это можно сделать на сайте в разделе «Азимут» или «Другие вычисления».
На заметку. Наиболее точные данные между точками предоставляют материалы и таблицы кадастровых справочников, но также в них много информации, лишней для обычного пользователя.
Откуда берется погрешность при расчете расстояния между точками
При вычислении прямого расстояния между координатами двух точек применяется подсчет длины дуги этих точек, для чего берется радиус точек (его приблизительное среднее значение ввиду особенностей формы Земли). Из-за этого возникает погрешность, т. е. мы получим не точную информацию о расстоянии между точками.
Чем больше искомое расстояние, тем больше получим погрешность в расстоянии между точками.
Также неточность между точками получим тогда, когда при вычислениях расстояния между точками берут недостаточно цифр после запятой в координатах: результат будет приблизительным.
Так, между любыми двумя известными точками на чертеже Земли можно проложить как обычное расстояние по дорогам, так и прямую линию, которая соединяет каждую точку. Вычисления точек проводят вручную или автоматически, причем во втором случае даже будет известна возможная погрешность, которую получим при нахождении расстояние между точками, неизбежная при измерении сферы Земли.
Если есть под рукой карта Google, то расстояние между двумя точками с заданными координатами можно определить с помощью инструмента Линейка.
Если этот инструмент не включен, его необходимо включить в Лаборатории карт (в левом нижнем углу).
Координаты можно нанести на карту либо через окно Поиск, либо через задание Маршрута.
Кратчайшим расстоянием между двумя точками на сфере является длина дуги большого круга (круга, проходящего через эти две точки и центр сферы). Эта дуга называется ортодромией (имеется в виду наименьшая из двух дуг большого круга).
Кратчайшее расстояние или длина ортодромии определяется формулой:
L = R x Y
где R – радиус сферы,
Y – центральный угол в радианах.
или
L = пR x A/180 град.
где A – центральный угол в градусах (п – “пи”).
Пусть x1, y1 и x2, y2 – широта и долгота двух точек. Тогда центральный угол будет определяться сферическим законом косинусов:
Y = arccos(sin x1 sin x2 + cos x1 cos x2 cos (y2 – y1))
Следует заметить, что при вычислениях с невысокой точностью (а тем более на калькуляторе) по этой формуле особенно при небольших расстояниях между точками, точность результата будет весьма невысока, поскольку центральный угол будет 0,99999999. (На 64-разрядных компьютерах точность может достигать нескольких метров на километр.)
Поэтому для практических целей применялись другие формулы с использованием синус-верзусов (обращенных синусов), которые сыграли важную роль в навигации.
В следующей формуле использован haversine (haversin, гаверсинус) – квадрат синуса половинного угла.
где переменные: кратчайшее расстояние, радиус сферы, широта и долгота точек соответственно.
Формула гаверсинусов хорошо работает при сравнительно небольших расстояниях, но имеет проблему для антиподов. Формула через арктангенс устраняет эту проблему. Центральный угол равен:
Для расчета расстояния между двумя точками по GPS-координатам можно написать несложную программу. Вот тут можно “поиграть” с программкой на JavaScript.
Для координат, приведенных в вопросе: L = 751 м. (эта цифра неплохо сходится с результатом, полученным Линейкой в картах Google).
Скрипты: JavaScript, Python
Координатный квест: как найти координаты и расстояния без регистраций и смс
Уровень сложности
Средний
Время на прочтение
11 мин
Количество просмотров 1.1K
Привет, Хабр!
С вами участник профессионального сообщества NTA Алексей Майка.
Хочу поделиться своим опытом решения одной интересной задачки и описать весь проделанный путь.
Был обычный денёк, сидел я на работе и занимался своими айтишными делами. Ко мне пришел руководитель и сказал: «Нужно рассчитать дистанцию до границы регионов для этих адресов». При этом без всяких платных сервисов и API онлайн карт, и своими усилиями. Айтишник понял, айтишник принял, айтишник получил свою заветную эксельку и пошёл работать.
План работы
-
Вступление
-
Начало начал
-
Кто? А главное, зачем?
-
Дёшево и сердито
-
Финишная прямая
-
Оценка качества
-
Итог
Вступление
Из школьных уроков географии я помнил, что для определения километража требуется знать координаты (широту и долготу) двух точек. И исходя из этого, я разделил задачу на 4 части:
-
поиск координат границы;
-
предобработка данных;
-
поиск координат адресов;
-
непосредственный расчёт расстояний между координатами.
В посте продемонстрировал весь путь решения данной задачи, небольшие нюансы, проверку результатов и непосредственно код. И, познакомив читателя с моей маленькой предысторией, расскажу об инструментах, которыми я пользовался.
В качестве основного инструмента для парсинга, обработки и расчётов я использовал Python. Средой разработки выступали Jupyter Notebook (Anaconda), PyCharm и DataSpell от компании JetBrains (дело вкуса). При работе с данным проектом использовал библиотеки Numpy, Pandas, Plotly, Geopy, Selenium.
На этом прелюдия заканчивается, переходим к сути.
Начало начал
Для расчёта дистанции до границы нужны координаты, что неудивительно, самой границы. Вручную прокликивать точки на карте мне не очень хотелось, а попытка поиска готовых координат полностью провалилась. К счастью, удалось найти json‑файл с положением границ субъектов России, среди которых и находятся нужные точки.
Для начала достаю нужные области. Импортирую библиотеки для дальнейшей работы, сохраняю данные файла в словарь (dict) и смотрю на содержимое объекта:
#Библиотека для работы с ".json"-файлами
import json
#Библиотека для обработки и анализа данных
import pandas as pd
#Библиотека для работы с многомерными массивами
import numpy as np
# Считываем файл с координатами всех регионов
with open('data//gadm41_RUS_1.json', encoding = 'utf-8') as js:
dict_coordin_border = json.load(js)
Видно, что json хорошо структурирован, и с ним достаточно легко работать. Названия регионов и координаты можно найти по следующим ключам:
-
dict_coordin_border[‘features’] [‘properties’][‘NL_NAME_1’] — название субъекта федерации;
-
dict_coordin_border[‘features’] [‘geometry’][‘coordinates’] — координаты границ субъектов.
Выделяю из данного словаря только нужные пять областей, и записываю в pandas.DataFrame данные, где:
-
region — название региона;
-
lon — долгота точки границы;
-
lat — широта точки границы;
-
sequence_number — порядковый номер записи;
-
color — цвет региона.
Зачем цвет и порядковый номер? Расскажу далее, а сейчас предлагаю рассмотреть код:
Посмотреть код
df_coord_reg = pd.DataFrame()
sequence_number = 0
for regions in dict_coordin_border['features']:
#Ставим условия для поля названия субъектов
if regions['properties']['NL_NAME_1'] in ['Воронежскаяобласть',
'Брянскаяобласть',
'Курскаяобласть',
'Ростовскаяобласть',
'Белгородскаяобласть']:
for list_coordin_lv_1 in regions['geometry']['coordinates']:
for list_coordin_lv_2 in list_coordin_lv_1:
for list_coordin_finish_lvl in list_coordin_lv_2:
#Заполняем df: Название региона, координаты точки границы, порядковый номер записи, цвет региона
if regions['properties']['NL_NAME_1'] == 'Воронежскаяобласть': color = 'purple'
elif regions['properties']['NL_NAME_1'] == 'Брянскаяобласть': color = 'white'
elif regions['properties']['NL_NAME_1'] == 'Курскаяобласть': color = 'blue'
elif regions['properties']['NL_NAME_1'] == 'Ростовскаяобласть': color = 'yellow'
elif regions['properties']['NL_NAME_1'] == 'Белгородскаяобласть': color = 'red'
df_coord_reg = df_coord_reg.append({'region': regions['properties']['NL_NAME_1'], #Название региона
'lon': list_coordin_finish_lvl[0], #Долгота точки границы
'lat': list_coordin_finish_lvl[1], #Широта точки границы
'sequence_number': str(sequence_number), #Порядковый номер записи
'color': color}, #Цвет региона
ignore_index = True)
sequence_number += 1
В итоге получается следующий dataframe:
На данном этапе я получил координаты границ регионов со всех сторон. Но это не совсем нужный результат, требуется только та часть границ, которые не совпадают друг с другом. И здесь я хочу рассказать про библиотеку plotly.
О plotly
Plotly — это графическая библиотека для интерактивной визуализации данных. С её помощью можно создавать диаграммы, гистограммы, карты распределения, 2D‑диаграммы, 3D‑графики и многое другое. Эта библиотека — сильный «зверь» для визуала, и она поможет расположить полученные точки на карте. Подробнее ознакомиться можно по ссылке.
Код ниже отображает точки на географической карте Европы:
Код
#Импортируем библиотеки для визуализации данных
import plotly.graph_objs as go
#Визуализируем на карте точки с координатами для проверки и дальнейшего анализа
fig = go.Figure(data=go.Scattergeo( #Scattergeo - данные, визуализируемые в виде точек географической карте
lon = df_coord_reg['lon'], #Долгота точки
lat = df_coord_reg['lat'], #Широта точки
mode = 'markers', #Вид точки
marker_color = df_coord_reg['color'], #Цвет точки
text = df_coord_reg['region'] + ' ' + df_coord_reg['sequence_number'] #Текст при наведении на точку
),
)
fig.update_layout(
title = 'Субъекты РФ ', #Задаем название карты
geo = dict(
scope='europe', #Шаблон карты
landcolor = "green", #Цвет для стран
countrycolor = "black", #Цвет границ между странами
),
width=1500, #Ширина карты
height=750 #Высота графика
)
Результат выполнения кода:
Как видно на рисунке, все точки находятся на своих местах. Осталось из них выбрать только точки, не являющиеся общими для регионов. Для этого я и задавал цвет областей и их порядковый номер.
Выбираю номера точек, которые находятся на границе, и перезаписываю данные в dataframe:
#Исходя из карты, выбираем следующие срезы df и записываем их в новую переменную
df_coord_border = pd.concat([df_coord_reg[11:411],
df_coord_reg[1278:1459],
df_coord_reg[974:1226],
df_coord_reg[3084:3157],
df_coord_reg[2004:2413]])
Для проверки повторно визуализирую данные и сохраняю полученные координаты в json‑файл.
Код
#Визуально проверяем полученный dataframe
fig = go.Figure(data=go.Scattergeo(lon = df_coord_border['lon'],
lat = df_coord_border['lat'],
mode = 'markers',
marker_color = df_coord_border['color'],
text = df_coord_border['region'] + ' ' + df_coord_border['sequence_number']))
fig.update_layout(
title = 'Субъекты РФ',
geo = dict(
scope='europe',
landcolor = "green",
countrycolor = "black",
),
width=1500, #Ширина карты
height=750 #Высота графика
)
#Сохраняем данные в json
df_coord_border[['lon', 'lat']].to_json('data//border.json')
Результат работы кода:
Кто? А главное, зачем?
Помните об эксельке, которую я упоминал в начале? Вот теперь пришло и её время. Создаю новую тетрадку, импортирую библиотеки, читаю xlsx‑файл. Смотрю на данные.
import pandas as pd
import numpy as np
import json
#Возьмем данные из входного файла
df_start_adress = pd.read_excel('data//starting_address.xlsx')
border_coord_df.head()
Что же получается? В файле хранятся 6 228 адресов, и, даже взглянув на эту выборку, закрадывается подозрение, что данные не имеют строгого формата. Необходимо удалить из dataframe дубликаты и проверить данные на пропуски:
#Удалим дубликаты
df_start_adress = df_start_adress.drop_duplicates()
df_start_adress
#None отсутствуют
df_start_adress.info()
#NaN,None и пустота могут быть в виде строки. Проверяем по длине строки
df_start_adress[df_start_adress['полный адрес'].str.len() <= 5]
К счастью, пропусков не наблюдается, а после удаления дубликатов dataframe сократился на 2 000 строк.
Проанализировав можно выделить несколько проблем:
-
Нет строгой типизации формата адресов. Это сильно ограничивает библиотеки и технологии, такие как запросы HTTP и бесплатные API сайтов.
-
В адресах присутствует подстрока «Адрес из Росреестра:». При проверке таких адресов в «Яндекс.Картах», выдается адрес отдела Росреестра города исходного адреса или пустой результат поиска:
-
Дублируются данные внутри ячеек. При проверке в онлайн картах данные не выдаются, или строится маршрут движения по этим адресам, точнее, путь от себя к себе:
-
Присутствуют лишние данные, которые мешают поиску.
Обработать полученные данные и привести их к одному виду показалось очень трудозатратной задачей. Проверив несколько адресов, я решил использовать сервис «Яндекс Карты». Он показал, что может работать с различными адресами и выдавать корректный результат.
Но обработать адреса все равно необходимо и избавиться хотя бы от некоторых проблем: подстрока с Росреестром и повторяющиеся данные.
Для этого применяю функцию formating_text. В функции создаю список, разбиваю строку на слова и помещаю их в список поочередно. Если данное слово уже существует в списке, то его в список не добавляю. В конце удаляю из итоговой строки «Росреестр»:
def formating_text(text):
old_text = text.split()
new_text = []
for word in old_text:
if word not in new_text:
new_text.append(word)
return ' '.join(new_text).replace('Адрес из Росреестра: ', '')
df_start_adress['formating_adress'] = df_start_adress['полный адрес'].apply(lambda x: formating_text(x))
Сохраняю полученный результат и перехожу к следующему этапу.
Дёшево и сердито
Самые внимательные читатели могли заметить, что для данного проекта я использовал библиотеку Selenium. Почему именно она? Она обладает преимуществами на фоне остальных библиотек и методов парсинга. Её ближайшие аналоги:
-
API «Яндекс.Карт». Данная система хоть и очень удобна в использовании для этой задачи, но она не бесплатна. А мы договаривались в начале публикации, никаких дополнительных вложений.
Тарифы Яндекса
-
Http/Https‑запросы. В отличие от системы, описанной выше, для запроса требуется только возможность подключения к нужному сайту. Но с моими данными написание get/ post-запросов — довольно сложная задача. К примеру, запрос https://yandex.ru/maps/213/moscow/house/mokhovaya_ulitsa_11s1 состоит из названий города, улицы и номера дома на транслите. Преобразовать входящие данные в такой формат будет непосильной задачей для меня.
Методом исключения осталась только библиотека Selenium, которая будет симулировать действие человека в браузере на сайте. Это позволит обойти системы защиты Яндекса, воспользоваться их алгоритмами обработки данных и найти координаты объекта.
Сразу скажу, здесь я не буду обучать вас данной библиотеке. Лишь разберу основной алгоритм работы скрипта, покажу некоторые нюансы и возможные методы их решения.
Подключение драйвера. В данном проекте я использовал браузер Google Chrome, и примеры будут для Google. Библиотека Selenium уже имеет в себе драйвер для работы с Google, и для его подключения просто нужно прописать команду webdriver.Chrome(). Для перехода на сайт используйте функцию get.
from selenium import webdriver
#Адрес сайта "Яндекс.Карты"
url_adress = 'https://yandex.ru/maps'
#Подключение драйвера Google
driver = webdriver.Chrome('chromedriver.exe')
#Переход на сайт
driver.get(url_adress)
time.sleep(5)
Поиск адреса. Адрес вписывается в поле формы поиска, код данного элемента “<input class=”input__control_bold” >”. Для поиска элемента использовал комбинацию функций WebDriverWait и ExpectedCondition. Selenium будет производить поиск элемента, пока он не будет найден или не кончится время ожидания.
Далее заполняю найденную форму адресом с помощью функции send_keys и запускаю поиск, имитируя нажатие клавиши Enter функцией send_keys(Keys.ENTER).
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Время ожидания
delay = 10
# Поиск формы ввода на сайте
elem_search_string = WebDriverWait(driver, delay)
.until(EC.presence_of_element_located(
(By.XPATH, "//input[@class='input__control _bold']")))
# Вписываем данные в форму
elem_search_string.send_keys(adress)
# Запускаем поиск
elem_search_string.send_keys(Keys.ENTER)
Результат поиска. Если все отработало штатно, Яндекс должен выдать географические координаты адреса. С помощью уже знакомой комбинации WebDriverWait и ExpectedCondition записываю координаты в переменную:
# Поиск координат на сайте
elem_search_2 = WebDriverWait(driver, delay)
.until(EC.presence_of_element_located(
(By.XPATH, "//div[@class='toponym-card-title-view__coords-badge']")))
# Запись в переменную координат адреса
coord = elem_search_2.text
Но Яндекс — не всемогущ, он не всегда находит однозначный ответ, поэтому на некоторые адреса он предлагает несколько вариантов. Как, например, здесь:
На случай таких ситуаций я брал первый предложенный вариант. Скорее всего он и будет являться нужным мне адресом:
elem_first_list = WebDriverWait(driver, delay)
.until(EC.presence_of_element_located(
(By.XPATH, "//div[@class='search-snippet-view__body _type_toponym']")))
elem_first_list.click()
И напоследок очищаю форму записи:
try:
elem_clear = WebDriverWait(driver, 2)
.until(EC.presence_of_element_located(
(By.XPATH, "//a[@class='small-search-form-view__pin']")))
except:
elem_clear = WebDriverWait(driver, 2)
.until(EC.presence_of_element_located(
(By.XPATH, "//div[@class='small-search-form-view__icon _type_close']")))
Рекомендую объединить поиск координат на сайте и очистку формы записи на случай, если сервис не найдет никакого результата. Это позволит продолжить работу скрипта и не перезапускать программу.
Повторяю все вышеперечисленные действия ещё 3 000 раз и сохраняю
результат в файл.
Финишная прямая
Осталось дело за малым: рассчитать дистанцию между координатами адресов и кратчайших точек построенной границы. С этим поможет библиотека Geopy.
О Geopy
Geopy — это сторонняя библиотека Python для определения географического местоположения. Она позволяет разработчикам Python легко находить координаты адресов, городов, стран и достопримечательностей по всему миру, используя сторонние геокодеры и другие источники данных. Ознакомиться с библиотекой можно по ссылке.
Для начала импортирую библиотеки и данные с файлов, которые получил ранее, координаты границы и адреса. Преобразую их в нужный формат для удобства в работе.
#Импортируем библиотеки
import pandas as pd
from geopy.distance import geodesic as GD
import json
from tqdm import tqdm
#Ипортируем данные с координатами
with open("data/adress_coord.json", 'r', encoding='utf-8-sig') as ad_cor:
adress_coord_dict = json.load(ad_cor)
border_coord_df = pd.read_json("data//border.json")
#Для удобства словарь с координатами адресов преобразуем в df
atress_coord_df = pd.DataFrame(adress_coord_dict.items(), columns=['adress', 'coord'])
# Преобразуем координаты границы в список
list_border_coord = list([(row['lat'], row['lon']) for index, row in border_coord_df.iterrows()])
Из библиотеки Geopy меня интересует только одна функция, которая как раз и рассчитает расстояние между двумя координатами — geodesic. Показываю, как она работает на примере:
Как видно из примера, в функцию нужно подавать широту и долготу в виде списка, множества, строки или кортежа. Главное, чтобы данные подавались попарно. В конце можно добавить единицу измерения расстояния: километры (km, kilometers), метры (m, meters), мили (mi, miles) и т. д.
Теперь пишу функцию, которая и будет считать минимальное расстояние между точками границы и адресом:
def distance_calculation(start_coord):
list_dist = []
for bord_coord in list_border_coord:
#Для расчета расстояния используем функцию GD([1 координаты точки],[2 координаты точки].[единица измерения расстояния])
dist = GD(start_coord, bord_coord).km
#Добовляем в список результат
list_dist.append(dist)
#Возвращаем минимальную дистанцию из списке
return min(list_dist)
tqdm.pandas()
atress_coord_df['dist_to_bor'] = atress_coord_df['coord'].progress_apply(lambda x: distance_calculation(x))
Результат:
Оценка качества
Для сдачи итоговых результатов нужно их проверить, ведь плохой результат никто не любит. Открываю Google Maps и адреса из первоначальной эксельки, расстояние до границы и линейку. И, как видно из рисунков, результаты корректны, а погрешность — в допустимых нормах.
Итог
Что я могу сказать по итогу? Задача необычная, интересная и в меру сложная. Попрактиковался с библиотеками Pandas, Selenium, Plotly и посмотрел на новую библиотеку Geopy. Результат работы корректен, а погрешность — в допустимых рамках. Санные пошли дальше в работу.
В общем, задача мне понравилась. Я получил дополнительный опыт и даже некие новые знания, и на этом я заканчиваю пост. Желаю всем удачи!)
P. S. Кстати, чуть не забыл, с кодом вы можете ознакомиться на Github.