GIS/PyQgis

PyQgis_카카오 로컬 API를 활용하여 시설 검색 및 불러오기, shp 파일 변환

조달송 2022. 6. 13. 16:39
728x90

오랜만에 포스팅을 합니다. 드물게 올라오는 포스팅에도 이래저래 방문해주시는 모든 분들께 감사드리며 시작하겠습니다:)

 

 

기존에 ArcMap을 쓰다가 qgis를 쓰면서 가장 아쉬웠던 점은 ArcMap에서 제공되던 다양한 분석을 할 수 없다는 것이었다. 그래서 관심 가지게 된 것이 PyQgis! Moran's I와 같은 기본적인 분석 방법을 이래저래 찾다보니 계속 파이썬 얘기가 나왔고,, 결국 이렇게 공부를 시작하게 됐다.

 

이번 포스팅은 PyQgis를 정말 친절하게 알려주고 계시는분의 유튜브와 github을 보고 현시점에서 업데이트겸 공부한 내용이다. 올려주신 내용을 바탕으로 정리하되 내가 공부하면서 이해한 내용 등을 추가한 것이기 때문에 이 영상을 먼저 보셨으면 좋겠다. 

참고한 유튜브 영상:  https://www.youtube.com/watch?v=Z7AKcBE1nlU

1. 카카오 개발자 앱키 받기

우리가 일상에서 활용하는 카카오맵이 다양한 정보를 제공하고있다. 카카오 개발자 홈페이지에서 제공하고 있으며, 데이터를 활용하기 위해서는 키를 받아와야한다! 이 과정에 대한 설명이 없어서 잘모르지만 그냥 일단 해보고 아래 내용들을 진행한 결과 문제없이 진행되었다. 

1) 먼저 홈페이지에 접속하여 [내 애플리케이션] 메뉴에 들어간다.

https://developers.kakao.com/docs/latest/ko/local/dev-guide#search-by-category

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

2) 애플리케이션 추가하기

앱이름과 본인 이름을 작성하여 입력해주기만 하면 API가 발급된다. 필자의 경우 앱을 개발하기 위한 용도는 아니여서 일단은 스터디그룹이름을 넣어서 작성했다. 

3) 인증키 확인하기

내 애플리케이션 중 입력한 애플리케이션을 클릭하면 4가지의 키정보(네이티브 앱 키, REST API 키, JavaScript키, Admin 키)가 뜬다. 그 중 이번에 활용하는 키는 REST API 키로 잘 복사해놓자!

 

여기까지 하면, 모든 사전 준비는 끝났다. 

 

2. 분석하고 싶은 자료 둘러보기

이번에 진행할 분석은, 특정 위치에서부터 반경 XXm 이내의 시설을 찾아내고 그것을 점 데이터로 변환하는 분석이다. 카카오 개발자 홈페이지 내에서 문서-로컬-개발 가이드(https://developers.kakao.com/docs/latest/ko/local/dev-guide) 에 들어가면 우리가 활용하고자 하는 데이터에 대한 정보를 쭉 설명해두었다. 그 중 우리는 [카테고리로 장소 검색하기]를 활용할 것이고, 이를 통해 장소명, 카테고리 이름, 전화번호, 지번 및 도로명 주소, 경위도 좌표 등을 받을 수 있다. 또한 카테고리 그룹 코드를 확인해서 분석하고 싶은 시설의 카테고리를 확인해야한다.

대형마트/편의점/ 어린이집 및 유치원 /학교/ 학원 / 주차장/ 주유소 및 충전소 / 지하철역/ 은행 /문화시설/중개업소/공공기관/관광명소/숙박/음식점/카페/병원/약국 카테고리로 나누어지며, 이번 분석에서는 편의점(CS2)과 카페(CE7)를 가지고 해보겠다.

 

3. 주피터 노트북과 Qgis에서 모듈 추가하기

카카오맵에서 API를 불러오기 위해서 필요한 모듈과, 판다스를 이용하기 위한 모듈을 추가해주어야 한다. 이때 Qgis에서도 아래의 코드를 똑같이 쳐주는데, 플러그인 탭-파이썬 콘솔을 선택하여 콘솔을 켜주면 된다. 

#모듈 추가
import requests 
from urllib.parse import urlparse #카카오맵에서 get방식으로 request할때 필요한 모듈
import pandas as pd

4. 주피터 노트북에서 코드 작성하기

이제 주피터 노트북에서 전반적인 작업을 진행한다.

lon = 126.976894; lat = 37.575654 # 궁금한 지역의 위경도 좌표(광화문 위치로 지정해봄)
page=1 # 첫번째 페이지(카카오 로컬API는 한페이지당 15개씩 결과값을 보여줌(기본값이며 최대 45페이지까지 보여줌))
url = "https://dapi.kakao.com/v2/local/search/category.json?&category_group_code=CS2&x="\  ###CS2= 편의점
+str(lon)+"&y="+str(lat)+"&page="+str(page)+"&radius=1000" # 카카오맵 API  / radius는 반경(최대 2000까지)
json_obj = requests.get(urlparse(url).geturl(),headers={"Authorization":"KakaoAK ##인증키 입력##"}).json() #개발자 인증키입력

먼저 자신이 분석하고자 하는 위치(반경 1km에 대한 분석 시 그 중심점)에 대한 경위도를 맨 첫줄에 작성해준다. 경위도는 구글지도에서 알아볼 수 있다. 필자의 경우 광화문을 중심으로 해보았다. 

다음으로는 페이지와 url을 입력하는데 이때 category_group_code=CS2&x=에서의 CS2가 분석하고자 하는 편의점이다. 만약 카페로 하고 싶을때는 CE7으로 바꿔주면 된다.

또한 url에서 radius=1000은 지정한 위치에서부터 1000m 반경을 의미하는 것이므로 원하는 반경에 따라 숫자를 변경해주면 된다.

마지막으로 json_obj는 api를 불러오는 과정으로 kakaoAK 뒤에는 자신이 받은 인증키(REST API 키)를 복붙해서 넣어주면 된다.

# 첫번째 편의점 정보 불러와보기
json_obj['documents'][0]

#불러온 정보를 pandas 형식으로 바꿔불러오기
pd.DataFrame(json_obj['documents'][0], index=[0])[['place_name', 'road_address_name', 'distance', 'x', 'y']]

# 반복문을 위한 데이터프레임 구성
df = pd.DataFrame(columns = ['place_name','road_address_name', 'distance', 'x', 'y']) #컬럼명이 있는 비어있는 데이터 프레임 설정
df_s = pd.DataFrame(json_obj['documents'][0], index=[0])[['place_name','road_address_name', 'distance', 'x', 'y']] #넣을 내용
df = df.append(df_s) # 반복해서 합쳐주기
df.head()

다음으로는 값을 불러와서 확인해본 후 pandas 형식으로 변환해준다. 또한 이 과정을 하나하나 할 수 없기 때문에 반복문을 위해 세번째 코드를 작성하셨다고 한다. df라는 컬럼명이 있지만 비어있는 데이터프레임을 작성한 후 json 형식의 데이터를 받아 df_s에 넣고, 이 과정을 반복해서 병합할 수 있도록 df.append를 쓰셨다. 하지만 이번에 실행해보니, .append 기능이 삭제될 예정이라고 한다. 그래서 아래와 같이 pd.concat을 활용하여 작성했다. 

###반복문의 pd.concat으로 수정해주기(df.append 곧 사용불가)
df = pd.DataFrame(columns = ['place_name','road_address_name', 'distance', 'x', 'y'])
df_s = pd.DataFrame(json_obj['documents'][0], index=[0])[['place_name','road_address_name', 'distance', 'x', 'y']]
df = pd.concat([df, df_s]) 
df.head()
# 메타정보 조건문 정의(몇페이지까지의 데이터를 가지고 있는지 확인) 
#is_end는 끝페이지까지 본건지 확인할 수 있는것. 즉 페이지를 쭈우욱 끌어오다가 마지막페이지면 반복문 종료
if json_obj['meta']['is_end'] == False:  
    print("False")
else:
    print("True")

한페이지씩 정보가 불러와지기 때문에, 반복문에서 페이지가 끝나면 반복을 종료할 수 있도록 하는 코드를 작성하셨고 이를 합쳐 아래의 함수를 작성하셨다. 

# 시설검색 함수 정의
def search_CVS(lon, lat):
    df = pd.DataFrame(columns = ['place_name','road_address_name', 'distance', 'x', 'y'])
    page = 1
    while True:
        url = "https://dapi.kakao.com/v2/local/search/category.json?&category_group_code=CS2&x="\
        +str(lon)+"&y="+str(lat)+"&page="+str(page)+"&radius=1000"
        json_obj = requests.get(urlparse(url).geturl(),headers={"Authorization":"KakaoAK a44f3e676d76f35cb86a24f6bd8d422c"}).json() #키 입력
        for document in json_obj['documents']:
            df_s = pd.DataFrame(document, index=[0])[['place_name','road_address_name', 'distance', 'x', 'y']]
            df = pd.concat([df, df_s]) 
        if json_obj['meta']['is_end'] == False:
            page += 1
        else:
            return df
# 편의점 검색 함수 테스트
df = search_CVS(126.976894, 37.575654)

# 편의점 정보 위의 5개만 불러와보기
df.head()

# 가장 가까운 편의점 확인
df.sort_values(by=['distance'], ascending=True).head()

# 편의점 개수
len(df)

위의 코드에서 CS2 부분을 CE7으로 바꾸면 카페에 대한 정보를 받을 수 있다. 또한 중심점을 바꾸어가면서 함수를 돌리면 더 많은 정보를 받을 수 있을 것 같다. 위의 유튜브 운영자분께서는 편의점 검색 함수를 이용해서 거리에 따라 정렬하여 가까운 편의점을 확인하고, 1000m이내의 편의점 개수를 확인하셨다. 

여기까지가 주피터 노트북 내에서 하는 내용이고, 추가로 파이썬 콘솔에서 돌릴 내용도 주피터노트북에서 작성해서 복붙하시는 형식으로 하셨다.  안그래도 파이썬 콘솔이 조금 달라서 어색했는데 이런식으로 작성한다는 것을 알았다..!

 

5. Qgis 파이썬 콘솔에서 쓸 코드 작성하고 점데이터로 불러오기

유튜브 운영자분께서 작성하신 코드에 영상 댓글에 달려있던 오류를 수정한 코드이다. 

# QGIS 파이썬콘솔: 편의점 검색 함수 정의
def search_CS2(lon, lat):
    df = pd.DataFrame(columns = ['place_name','road_address_name', 'distance', 'x', 'y'])
    page = 1
    while True:
        url = "https://dapi.kakao.com/v2/local/search/category.json?&category_group_code=CS2&x="\
        +str(lon)+"&y="+str(lat)+"&page="+str(page)+"&radius=1000"
        json_obj = requests.get(urlparse(url).geturl(),headers={"Authorization":"KakaoAK a44f3e676d76f35cb86a24f6bd8d422c"}).json()
        for document in json_obj['documents']:
            df_s = pd.DataFrame(document, index=[0])[['place_name','road_address_name', 'distance', 'x', 'y']]
            df = df.append(df_s)
        if json_obj['meta']['is_end'] == False:
            page += 1
        else:
            vl = QgsVectorLayer("Point?crs=EPSG:4326", "CVS", "memory")
            pr = vl.dataProvider()
            pr.addAttributes([QgsField("place_name", QVariant.String),
                              QgsField("road_address_name", QVariant.String),
                              QgsField("distance",  QVariant.Int),
                              QgsField("lon", QVariant.Double),
                              QgsField("lat", QVariant.Double)])
            vl.updateFields()
            for i in range(len(df)):
                f = QgsFeature()
                f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(float(df.iloc[i, 3]), float(df.iloc[i, 4]))))
                f.setAttributes([df.iloc[i, 0], df.iloc[i, 1], df.iloc[i, 2], df.iloc[i, 4], df.iloc[i, 3]])
                pr.addFeature(f)
            vl.updateExtents()
            QgsProject.instance().addMapLayer(vl)
            break

여기서 주의해야할 것은, While 문안에 있는 인증키를 자신의 것으로 입력해야한다는 것, 반경이나 시설을 변경할 경우에도 잘 확인해야한다는 것이다! 파이썬콘솔 코드를 처음봐서 잘은 모르겠지만, 해석해보자면 다음과 같다.

먼저 else 부분을 보면, QgsVectorLayer은 포인트로 된 벡터레이어를 작성하되 좌표계는 EPSG:4326이고, 레이어 이름은 'CVS', memory이므로 임시 레이어를 작성한다는 뜻이다. addAttributes에서 CVS 벡터레이어의 속성값들을 지정해주는 것이다. 

이렇게 작성한 코드를 복사해서 파이썬 콘솔에서 실행해주면 된다. 

Qgis 파이썬 콘솔에서 복붙하되, 코드가 길기 때문에 편집기를 켜서 실행해주는게 좋다고 한다. 필자의 경우 편의점을 레이어 이름으로 설정해서 실행해보았다. 함수를 입력해준 후, 원하는 위치(광화문)을 입력해주면 된다. 그러면 아래와 같이 편의점 이름의 레이어가 생성된다. 데이터를 확인해 본 후 임시데이터가 아닌 shp 파일로 내보내기 하면 된다. 

다음으로 카페를 대상으로 진행해보았다. 함수명을 search_CE7으로 변경해주고 카테고리 그룹코드도 CE7으로, 레이어명은 카페로 변경하였고 아래와 같은 결과물이 나왔다. 

공공에서 제공하는 데이터가 잘 없어서 API를 쓰고 싶었던 때가 많은데 이렇게 활용할 수 있다니 신세계가 열린기분이다..!

다시한번 이렇게 소중한 정보를 알려주신 유튜브 운영자분께 감사드린다!ㅎㅎ최고!

반응형