파이썬/Basic

Geopy : 지리 정보 다루기

코샵 2025. 2. 10. 10:09
반응형

Geopy는 파이썬에서 지리 정보를 쉽게 다룰 수 있게 해주는 강력한 라이브러리입니다. 주소를 좌표로 변환하거나, 두 지점 간의 거리를 계산하는 등 다양한 지리 정보 처리가 가능합니다.

기본 설치 및 설정

# 설치
pip install geopy

# 기본 임포트
from geopy.geocoders import Nominatim
from geopy.distance import geodesic
from geopy.exc import GeocoderTimedOut

주소를 좌표로 변환하기 (Geocoding)

from geopy.geocoders import Nominatim

def get_coordinates(address: str):
    # 지오코더 초기화
    geolocator = Nominatim(user_agent="my_app")

    try:
        # 주소로 위치 검색
        location = geolocator.geocode(address)

        if location:
            return {
                'address': location.address,
                'latitude': location.latitude,
                'longitude': location.longitude
            }
        return None

    except GeocoderTimedOut:
        return None

# 사용 예시
address = "서울특별시 강남구 테헤란로 427"
coordinates = get_coordinates(address)
print(coordinates)

좌표로 주소 찾기 (Reverse Geocoding)

def get_address(latitude: float, longitude: float):
    geolocator = Nominatim(user_agent="my_app")

    try:
        location = geolocator.reverse((latitude, longitude))
        return location.address
    except GeocoderTimedOut:
        return None

# 사용 예시
latitude = 37.5665
longitude = 126.9780
address = get_address(latitude, longitude)
print(address)

두 지점 간의 거리 계산

from geopy.distance import geodesic

def calculate_distance(point1: tuple, point2: tuple):
    """
    두 지점 간의 거리를 킬로미터 단위로 계산
    point1, point2: (latitude, longitude) 형식의 튜플
    """
    distance = geodesic(point1, point2).kilometers
    return round(distance, 2)

# 사용 예시
seoul = (37.5665, 126.9780)
busan = (35.1796, 129.0756)
distance = calculate_distance(seoul, busan)
print(f"서울-부산 거리: {distance}km")

다양한 지오코더 활용

from geopy.geocoders import Nominatim, GoogleV3, ArcGIS

class GeocodingService:
    def __init__(self, service='nominatim', api_key=None):
        if service == 'nominatim':
            self.geolocator = Nominatim(user_agent="my_app")
        elif service == 'google':
            self.geolocator = GoogleV3(api_key=api_key)
        elif service == 'arcgis':
            self.geolocator = ArcGIS()
        else:
            raise ValueError("Unsupported geocoding service")

    def geocode(self, address):
        try:
            location = self.geolocator.geocode(address)
            if location:
                return {
                    'address': location.address,
                    'latitude': location.latitude,
                    'longitude': location.longitude
                }
        except Exception as e:
            print(f"Error: {e}")
            return None

실전 활용 예시: 주변 장소 찾기

def find_nearby_locations(center_point: tuple, locations: list, max_distance: float):
    """
    주어진 중심점으로부터 특정 거리 내의 장소들을 찾습니다.

    Args:
        center_point: (latitude, longitude) 형식의 중심점
        locations: [(name, lat, lon)] 형식의 장소 리스트
        max_distance: 최대 거리 (km)
    """
    nearby = []

    for name, lat, lon in locations:
        point = (lat, lon)
        distance = calculate_distance(center_point, point)

        if distance <= max_distance:
            nearby.append({
                'name': name,
                'distance': distance,
                'coordinates': point
            })

    return sorted(nearby, key=lambda x: x['distance'])

# 사용 예시
my_location = (37.5665, 126.9780)
places = [
    ("명동", 37.5634, 126.9850),
    ("강남역", 37.4980, 127.0278),
    ("이태원", 37.5340, 126.9940)
]

nearby_places = find_nearby_locations(my_location, places, 5.0)
for place in nearby_places:
    print(f"{place['name']}: {place['distance']}km")

에러 처리와 최적화

from functools import lru_cache
import time

class GeocodingManager:
    def __init__(self, retry_count=3, retry_delay=1):
        self.geolocator = Nominatim(user_agent="my_app")
        self.retry_count = retry_count
        self.retry_delay = retry_delay

    @lru_cache(maxsize=100)
    def geocode_with_retry(self, address: str):
        """
        주소 검색을 재시도하며 결과를 캐싱합니다.
        """
        for attempt in range(self.retry_count):
            try:
                location = self.geolocator.geocode(address)
                if location:
                    return {
                        'address': location.address,
                        'latitude': location.latitude,
                        'longitude': location.longitude
                    }
            except GeocoderTimedOut:
                if attempt < self.retry_count - 1:
                    time.sleep(self.retry_delay)
                continue
            except Exception as e:
                print(f"Error: {e}")
                break
        return None

유용한 지리 정보 처리 유틸리티

def get_bounding_box(center: tuple, radius_km: float):
    """
    중심점으로부터 특정 반경의 경계 상자를 계산합니다.
    """
    from math import radians, degrees, cos, sin, asin, sqrt

    lat, lon = center
    radius = radius_km / 111.32  # 1도당 약 111.32km

    min_lat = lat - radius
    max_lat = lat + radius

    # 위도에 따른 경도 범위 조정
    radius_lon = radius / cos(radians(lat))
    min_lon = lon - radius_lon
    max_lon = lon + radius_lon

    return {
        'min_lat': min_lat,
        'max_lat': max_lat,
        'min_lon': min_lon,
        'max_lon': max_lon
    }

# 사용 예시
seoul_center = (37.5665, 126.9780)
search_area = get_bounding_box(seoul_center, 5.0)  # 5km 반경

Geopy는 지리 정보 처리에 필수적인 도구이며, 특히 위치 기반 서비스를 개발할 때 매우 유용합니다. API 제한과 에러 처리에 주의하면서 사용하면 효과적인 지리 정보 처리 시스템을 구축할 수 있습니다.