#!/usr/bin/env python # coding: utf-8 # In[1]: #!pip3 install geopandas #!pip3 install geopy #!pip3 install folium #!pip3 install branca # # Часть 1. Прямой геокодинг адресов из базы, подготовка датасета для дальнейшей визуализации на карте # > ## 1.1 geopy - основная билбиотека, которая будет использоваться в проекте для прямого геокодирования с помощью API Yandex # In[2]: import numpy as np import pandas as pd import time from pprint import pprint from geopy.geocoders import ArcGIS,Yandex from geopy.extra.rate_limiter import RateLimiter # instantiate a new Nominatim client app = ArcGIS(user_agent="tutorial") import warnings warnings.filterwarnings('ignore') # > ## 1.2 Так как прямое геокодирование большого количества адресов достаточно долгая процедура, в качестве теста, добавим оповещение в Telegramm (это позволит в цикле каждые, например, 10 000 адресов, отправлять сообщение с статусом работы) # In[3]: import requests # telegram url bot_token = "TelegramBotApiToken" chat_id = "TelegramChatID" def send_mess(text): params = {'chat_id':chat_id, 'text': text} response = requests.post('https://api.telegram.org/bot'+ bot_token + '/sendMessage', data=params) return response send_mess("Hello world!") # > ## 1.3 Загрузка первичной базы с адресами 500 строк (Публичный источник: Адресный справочник Челябинска - https://t.domspravka.com/, дополнительно поля name и birth изменены) # In[4]: data = pd.read_csv("D:\\data_test.csv", sep=";",encoding="windows-1251",index_col=False)#cp866 # In[5]: data.head(3) # In[6]: data.info() # In[7]: #Очистка от аномалий и форматирование полей в датафрейме data['name'] = data['name'].astype(str) data['name'] = data['name'].str.replace('/n','') data['adress'] = data['adress'].str.replace('/n','') #data = data.infer_objects() #Для более точного прямого геокодирования добавим значения страны, области и города в строку адреса data['adress']= "РОССИЯ, Челябинская область, г. Челябинск"+ ", " +data['adress'] # In[8]: #Пример дополненного адреса data['adress'][1] # > ## 1.4 Подключение Геокодер API Яндекс.Карт и проверка работы прямого геокодирования Яндекс API (https://yandex.ru/dev/maps/geocoder/) # In[9]: #https://pikabu.ru/story/yandeks_baunti_ili_klyuch_za_million_besplatno_7737687 может помочь app = Yandex(user_agent="tutorial",api_key="ApiKey") location = app.geocode("454080 ПР-КТ. ЛЕНИНА, д. 83") #Тест работы прямого геокодинга pprint(location) # min_delay_seconds - Задержка на вызов (таким образом исключаем ошибки при слишком большой скорости запросов к геокодеру) geocode = RateLimiter(app.geocode, min_delay_seconds=0.3) # In[10]: #Создаем новый столбец для записи совокупных данных геокодера data['location'] = 0 data['location'] = data['location'].astype(str) #Создаем новые столбцы для конкретных числовых значений определенных координат data['latitude'] = 0 data['longitude'] = 0 data['altitude'] = 0 data['point'] = 0 # > ## 1.5 Применение геокодера Яндекс для получения координат адресов из загруженного списка # In[11]: # Прямой геокодинг for i in range(1,len(data)): if (data['location'][i-1]==data['location'][i] and data['location'][i-1]!='0'):#Проверяем не совпадает ли следующая строка с предыдущей, если одинаковые, то просто приравниваем их друг к другу data['location'][i-1]=data['location'][i] else: data['location'][i:(i+1)] = data['adress'][i:(i+1)].apply(geocode) #Цикличное геокодирование data['point'][i:(i+1)] = data['location'][i:(i+1)].apply(lambda loc: tuple(loc.point) if loc else (0.0,0.0,0.0)) #None) #if (i % 10==0): # print (data[['location']][(i-10):i]) if (i % 100==0):#Актуально для 10 000 и более, 100 взято в качестве теста send_mess("("+ str(i) + ") строк обработано") #Сообщение с статусом в Telegram чат # > # 1.6 Заполнение полей конкретными числовыми значениями координат (разделение столбца 'point' на столбцы 'latitude', 'longitude','altitude') # In[12]: data['latitude']=data['latitude'].astype(float) data['longitude']=data['longitude'].astype(float) data['altitude']=data['altitude'].astype(float) a = data['point'].tolist()# Формирование из столбца датафрейма 'point' списка с значениями b = [] z = 0 for n in range(1,len(data)): b.append(str(a[n]).split(','))# Дробим список с значениями 'point' по запятым и записываем части в отдельные столбцы data['latitude'][n]=float(b[n-1][0][1:50]) data['longitude'][n]=float(b[n-1][1][1:50]) data['altitude'][n]=float(b[n-1][2][1:3]) #print(n,data['latitude'][n],data['longitude'][n]) # In[13]: data.head()# Итоговый датафрейм с данными # # Часть 2. Отрисовка полученных координат на карте с группами маркеров # > ## 2.1 folium - основная билбиотека, которая будет использоваться в проекте для создания интерактивных карт # In[14]: import folium from folium.plugins import FastMarkerCluster from folium.plugins import MarkerCluster from folium.plugins import Search from folium import FeatureGroup import branca from datetime import timedelta, datetime # In[15]: # 1 - Добавляем столбец с именем и номером квартиры (для дальнейшего облегчения поиска по фильтру на карте) # 2 - Сортируем датафрейм по № квартиры и адресу (для дальнейшего удобного отображения марекров в составе кластера маркеров) data.sort_values(by=['kv','adress']) data['name_kv'] = data['name']+" //"+data['kv'] data['emailkv'] = data['example_feature']+" //"+data['kv'] # > ## 2.2 Создание функции для формирования HTML таблиц с значениями из датафрейма (при клике по значку на карте откроется данная таблица с детальной информацией. Формат приведен для примера, реально применение всей палитры возможностей языка HTML) # In[16]: def fancy_html(row): i = row FIO = data['name'].iloc[i] age = data['birth'].iloc[i] adress = data['adress'].iloc[i] adress_geo = data['location'].iloc[i] kv = data['kv'].iloc[i] email = data['example_feature'].iloc[i] left_col_colour = "#2A799C" right_col_colour = "#C5DCE7" # Простая HTML таблица с данными из датафрейма html = """
ФИО жильца | {} | """.format(FIO) + """
Год рождения | {} | """.format(age) + """
Номер помещения | {} | """.format(kv) + """
{} | """.format(email) + """|
Адрес(полный) | {} | """.format(adress) + """
Адрес по геодекодингу | {} | """.format(adress_geo) + """