7. Tip & Trick

지오코딩(주소로 좌표 및 포인트 shp 변환하기) pandas geopandas numpy 활용

yongmuni 2024. 10. 19. 17:05

Gis 에서 주소데이터를 포인트로 변환해야 할 경우가 있다.

 

파이썬을 사용해서 이 과정을 거치면 쉽게 포인트shp를 얻을 수 있다.

 

먼저 주소 데이터를 파일에 넣고 vscode같은 ide 로 불러온 다음, pandas, geopandas, numpy 를 import 한다.

import pandas as pd
import geopandas as gpd
import numpy as np
import os

file_link = "C:/Users/...."
os.listdir()

먼저 폴더안의 파일명들을 확인해보자.

주소 csv 파일이 있다.

 

csv 파일을 DataFrame 으로 저장하자.

df = pd.read_csv('C:/Users/보건소_경로당_수정.csv'
df.head()

데이터프레임이 주소를 포함하여 잘 생성이 되었다.

 

이제 브이월드의 api 를 활용하여 지오코딩을 사용해보자

 

먼저 브이월드 사이트에 회원 가입을 하여 인증키를 받아준다. 

https://www.vworld.kr

 

브이월드

국가가 보유하고 있는 공개 가능한 공간정보를 모든 국민이 자유롭게 활용할 수 있도록 다양한 방법을 제공합니다.

www.vworld.kr

로그인 후 오픈 API - 인증키 발급 - 사용목적: '지오코딩' '개인용' 등 목적에 맞게 입력하고 아래에 활용 api에 지오코더 API에 체크한다.

그런 다음 발급을 누르고 다시 [인증키 관리] 에 들어가면 인증키가 발급된 것을 알 수 있다.

인증키를 복사해서 메모장에 입력해놓은 다음

 

지오코더 api의 레퍼런스를 찾아보자 api레퍼런스에 들어가서

 버전 2를 누르고  [사용예제]의 파이썬을 눌러 사용법을 알아보면

다음과 같음을 알 수 있다.

 

이제 이를 복사해서 vsc 에 가져와 보자

import requests
apiurl = "https://api.vworld.kr/req/address?"
params = {
	"service": "address",
	"request": "getcoord",
	"crs": "epsg:4326",
	"address": "판교로 242",
	"format": "json",
	"type": "road",
	"key": "DA501CasdfasdfaEEEEEEDDDE"
}
response = requests.get(apiurl, params=params)
#if response.status_code == 200:
#print(response.json())

data = response.json()
data

 

key의 인증키를 입력하고 아래에 

requests 라이브러리를 사용하여 url에서 api를 받아주었다.

응답 결과를 확인하기 위해 노트북에 data를 출력 해보면

{'response': {'service': {'name': 'address',
   'version': '2.0',
   'operation': 'getcoord',
   'time': '17(ms)'},
  'status': 'OK',
  'input': {'type': 'road', 'address': '판교로 242'},
  'refined': {'text': '경기도 성남시 분당구 판교로 242 (삼평동)',
   'structure': {'level0': '대한민국',
    'level1': '경기도',
    'level2': '성남시 분당구',
    'level3': '삼평동',
    'level4L': '판교로',
    'level4LC': '',
    'level4A': '삼평동',
    'level4AC': '4113565500',
    'level5': '242',
    'detail': ''}},
  'result': {'crs': 'EPSG:4326',
   'point': {'x': '127.101313354', 'y': '37.402352535'}}}}

다음과 같은 응답을 받을 수 있는데, 아래에 'point' 키를 보면 x와 y좌표를 받고 있음을 알 수 있다.

 

이는 'result' 키안에 들어있는 것을 알 수 있다.

 

이제 우리는 reponse의 x와 y좌표를 가지고 shp를 생성 할 것이다.

 

새로운 ipynb 파일을 준비하고 함수를 만들어 보자. 

#이 함수는 대구 빅데이터 센터 유튜브의 강의를 참고했다.

import numpy as np
import pandas as pd
import requests

def geocoder(address):
    x, y = np.NaN, np.NaN
    url = "https://api.vworld.kr/req/address?"
    params = {
        "service": "address",
        "request": "getcoord",
        "crs": "epsg:4326",  # 좌표계 설정 (WGS84)
        "address": str(address),
        "format": "json",
        "type": "road",  # 도로명 주소 사용
        "key": "DAeeeee23452sfsdfq34",
        "refine": "true"
    }

    res = requests.get(url, params=params)
    
    # 응답 내용을 출력하여 확인
  #  print(res.text)  # JSON으로 변환 전에 응답 텍스트를 확인
    
    if res.status_code == 200:
        try:
            data = res.json()
            x = data["response"]["result"]["point"]["x"]
            y = data["response"]["result"]["point"]["y"]
        except :
            pass
            
    return pd.Series([x, y])

이 함수는 주어진 주소(address)를 기반으로 vWorld API를 사용하여 해당 위치의 좌표(경도와 위도)를 반환하는 함수다. 이 함수는 다음을 수행한다.

 

  • 주소 정보를 API 요청 매개변수로 설정하여 URL생성
  • requests 라이브러리를 사용하여 API에 요청을 보냄
  • 응답이 성공적(status_code == 200)인 경우 JSON 데이터를 파싱하여 결과에서 경도(x)와 위도(y) 값을 추출
  • pandas.Series로 좌표를 반환

 

 

이제 함수를 실행해보자. 

주소 데이터 프레임에 x와 y필드를 추가하여 각 행에 시리즈로 입력해보자

df[["x","y"]] = df['도로명주소'].apply(geocoder)
df.head()

도로명주소 필드 우측에 함수의 결과 값인 x와 y  필드가 추가된 모습을 볼 수 있다.

 

이제 제대로 좌표가 추출되지 않은 객체를 찾아보자.

fail = df[df["x"].isna()]
fail.head()

 

 

작업 중인 폴더에 fail 파일을 csv 로 저장 

fail.to_csv('fail.csv', index = False)

 

 

지오코딩 되지 않은 주소들은

fail 데이터프레임을 csv 로 저장해서 

구글 스프레드시트에서 지오코딩을 해보자.

 

총 9개의 데이터가 지오코딩이 되지 않았다.

스프레드 시트 상단의 확장프로그램의 부가기능에서 geocode by awesome table 을 검색해서 설치해준다.

다시 확장프로그램 - geocode by awesome table -start geocoding 을 눌러보자

 

아래에 시트를 선택해주고 주소 열을 설정후 시작버튼을 누르면 자동으로 위도 경도가 입력된다.

하지만 하루에 생성되는 주소의 개수가 제한적인것 같다. 그래서 많은 데이터를 한번에 돌리는 것은 추천하지 않는다.

 

위도와 경도가 입력이 되었다.

 

이제 이파일을 fail-fail.csv로 저장해서 다시 vscode로 가져오자.

fail = pd.read_csv("C:/Users/이용현/Desktop/프로젝트/fail - fail.csv")
fail.head()

가져와졌다.

 

이제 x,y 열을 위도와 경도 열이름을 바꿔보자 

fail = fail.drop(["x","y"], axis =1)
fail = fail.rename(columns={"Latitude" : "y", "Longitude" : "x"})
fail.head()

잘바뀌었다.

 

이제 위의 df( 지오코딩이 완료된, 완료되지 않은 데이터가 같이 있는 데이터프레임) 을 fail ( 지오코딩 실패했지만 구글 확장프로그램으로 완료된 데이터 프레임) 을 합쳐보자.

 

df = df[df["x"].notna()]
result = pd.concat([df, fail],axis =0 )
result

 

 df가 지오코딩이 안된 행들도 가지고 있기 때문에 notna() 메서드를 사용해서 결측치를 드랍해준 다음, 

pd.concat( ) 을 사용해서 두 데이터 프레임을 합쳐준다.

 

1204 행으로 처음 주소 데이터의 행개수와 같다.

 

이제 geometry 를 사용하여 geodataframe 을 생성해보자.

*먼저 geopandas 를 import 해야한다.

geometry = gpd.points_from_xy(result['x'], result['y'])
gdf = gpd.GeoDataFrame(result, geometry=geometry, crs=4326)
gdf.head()

우측에 geometry 가 생성되었다. 좌표계는 4326 이다. 

 

이제 이 geodataframe 을 shp 나 gpkg 로 바꿔서 gis에 올려 사용하면 된다.

gdf.to_file("C:/Users/보건소 경로당_지오코딩.gpkg", driver="GPKG", encoding='euc-kr')

 

잘 올라가는 지 qgis에서 확인 해보자.

 

 

여러개 확인해볼 시 정확한 자리에 경로당이 위치한 것 같다.

 

지오코딩이 완료 되었다. 원본은 항상 저장해 두고 활용하자.