Geo data in Python
Понадобилось мне недавно нарисовать в Python данные на карте, благо в данных есть координаты. Казалось бы, что может быть сложного. Но обо всем по порядку.
Мои точки — это города. Города имеют разные признаки, как качественные (используем разные цвета точек на карте), так и количественные (используем разный размер кружочка).
Первым делом нужно установить и импортнуть библиотеку geopandas — она показалась мне самой простой и понятной среди всех вариантов работы с геоданными.
Нужно нарисовать контуры России. Для этого нужен файл с границами — geojson. Его можно очень быстро нагуглить (я взяла отсюда). Мне нужны лишь контуры страны, поэтому я взяла файл 2 уровня — admin_level_2.geojson
Этот файл нужно прочитать в dataframe. Так как у меня jupyterhub поднят на сервере, мне понадобилось сначала файл загрузить в jh. Далее конвертируем его в GeoDataFrame и отрисовываем. Получился следующий код.
map_df=gpd.read_file('my_env/admin_level_2.geojson') map_df=gpd.GeoDataFrame(map_df) map_df.plot(figsize=(14,10), color='black', alpha=0.1)
3 простых шага, которых достаточно достаточно почти в любой стране мире. Но есть одно «но» — Россия реально большая страна.
Настолько большая, что переходит границу 180 о и из-за этого стандартная отрисовка рисует бедный дальневосточный кусок не справа, а далеко слева.
Поэтому сначала нужно сделать преобразование координат, прибавив к оторвавшемуся куску 360 о .
Я рассмотрела файл поближе и выяснила, что он состоит из 29 полигонов. Прибавить к нужным полигонам 360 о — дело не сложное. Запихиваем в цикл по датафрейму и сдвигаем полигоны с отрицательными координатами.
Но нет. Тут мой код зависал всерьез и надолго. Геоданные — огромный тяжелый массив и перелопачивать их все jh явно не желал. Недолго думая я приняла решение пожертвовать Кемской волостью мелкими полигончиками.
Я отрисовала все полигоны по отдельности и выяснила, что основная часть — это полигон 28. Отрезанный кусок дальнего востока — 26. Сдвинуть один на 360 о и отрисовать только эти два полигона занимает буквально секунду. Итоговый код занимает 5 строчек, а результат вполне меня устраивает.
map_df=gpd.read_file('my_env/admin_level_2.geojson') map_df=gpd.GeoDataFrame(map_df) p = gpd.GeoSeries(shapely.affinity.translate(map_df['geometry'][0][26], xoff=360, yoff=0)) p=p.append(gpd.GeoSeries(map_df['geometry'][0][28])) p.plot(figsize=(14,10), color='black', alpha=0.1)
Теперь займемся данными, ради которых мы всё это начали. Не забываем, что в выгружаемых данных тоже возможно есть данные с отрицательными координатами и их так же надо сдвинуть на 360 для нормальной отрисовки.
select city ,lat ,case when lon
Преобразуем поля широты и долготы в одно поле "координаты" и преобразуем DataFrame в GeoDataFrame.
df['coordinates'] = df[['lon', 'lat']].values.tolist() df['coordinates'] = df['coordinates'].apply(Point) df = gpd.GeoDataFrame(df, geometry='coordinates')
Всё. Осталось только нарисовать оба набора данных вместе. Важно помещать обе строки в одну ячейку jupyterhub'а, иначе не сработает.
ax_all=p.plot(figsize=(14,10), color='black', alpha=0.1) df.plot(ax=ax_all, column = 'category',markersize=20,marker='o')
Или пример с размером кружочков (на урезанном наборе данных для наглядности).
ax_all=p.plot(figsize=(14,10), color='black', alpha=0.1) df_size.plot(cmap='autumn',ax=ax_all, markersize='qty',marker='o',alpha=0.6)
Далее можно наводить красоту, но основная задача выполнена. Кроме того этого вполне достаточно для быстрого анализа данных, а именно это нам обычно и нужно от Python.
Geocoding in Python Using Geopy
Have you ever come across a dataset having addresses like below? How do you handle the address column? If the dataset is large and if you try to encode the address column, it will result in high cardinality issue.
As part of the feature engineering, you can handle this in many ways. For example, you can extract country, city, and area from the given addresses and use them as features. Another way is to geocode the addresses into geographical coordinates (latitude and longitudes) and use them as features.
Some of the popular packages that are used for geocoding and reverse geocoding in Python are geopy, geocoder, opencage, etc.
In this article, you will first understand what geocoding and reverse geocoding are, and then explore the geopy package to convert addresses into latitudes and longitudes and vice versa. And finally, we will see how to calculate the distance between the two addresses.
Geocoding
Geocoding is the process of converting addresses into geographic coordinates (i.e. latitude and longitude).
Reverse Geocoding
Reverse Geocoding is the process of converting geographic coordinates (latitude & longitude) into a human-readable address.
Geocoding and reverse geocoding are provided by different service providers such as OpenStreetMap, Bing, Google, AzureMaps, etc. These services provide APIs which can be used by anyone. However, each of these incurs costs for using their services and comes with limitations of their own.
Geopy
The geopy package is not geocoding service provider. It just provides an interface to connect to several services under a single package.
Below is the list of all the services that are implemented in geopy. You can use any of these geocoder services but keep in mind that each service comes with its own terms of conditions, pricing, API keys, etc. The OpenStreetMap service is free so we’ll be using the Nominatim service in this article.
If you don’t want to use geopy, then you can directly use the API provided by the above services. For example, you can use Google Geocoding API directly instead of geopy.
How to get the longitude and latitude of a city using Python?
To get the longitude and latitude of a city, we will use the geopy module. geopy uses third-party geocoders and other data sources to locate the coordinates of addresses, cities, countries, etc.
First of all, make sure the geopy module is installed −
In the following example, we will use the Nominatim geocoder to find the longitude and latitude of the city "Hyderabad".
Steps −
- Import Nominatim geocoder from geopy module.
- Initialize the Nominatim API and use the geocode method to get the location of the input string.
- Finally, get the latitude and longitude of the location by location.latitude and location.longitude.
Example 1
# Import the required library from geopy.geocoders import Nominatim # Initialize Nominatim API geolocator = Nominatim(user_agent="MyApp") location = geolocator.geocode("Hyderabad") print("The latitude of the location is: ", location.latitude) print("The longitude of the location is: ", location.longitude)
Output
It will print the following output on the console −
The latitude of the location is: 17.360589 The longitude of the location is: 78.4740613
In this example, let's do the opposite of Example 1. We will start by providing a set of coordinates and find the city, state, and country those coordinates represent. Instead of printing the output on the console, we will create a tkinter window with four labels to display the output.
Steps −
- Initialize the Nominatium API.
- Use the geolocator.reverse() function and supply the coordinates (latitude and longitude) to get the location data.
- Get the address of the location using location.raw['address'] and traverse the data to find the city, state, and country using address.get().
- Create labels inside a tkinter window to display the data.
Example 2
from tkinter import * from geopy.geocoders import Nominatim # Create an instance of tkinter frame win = Tk() # Define geometry of the window win.geometry("700x350") # Initialize Nominatim API geolocator = Nominatim(user_agent="MyApp") # Latitude & Longitude input coordinates = "17.3850 , 78.4867" location = geolocator.reverse(coordinates) address = location.raw['address'] # Traverse the data city = address.get('city', '') state = address.get('state', '') country = address.get('country', '') # Create a Label widget label1=Label(text="Given Latitude and Longitude: " + coordinates, font=("Calibri", 24, "bold")) label1.pack(pady=20) label2=Label(text="The city is: " + city, font=("Calibri", 24, "bold")) label2.pack(pady=20) label3=Label(text="The state is: " + state, font=("Calibri", 24, "bold")) label3.pack(pady=20) label4=Label(text="The country is: " + country, font=("Calibri", 24, "bold")) label4.pack(pady=20) win.mainloop()
Output
It will produce the following output −