Python Folium을 사용해서, 대한민국 법정동 경계선 시각화해보기

h-go-getter·2023년 12월 10일
6
post-thumbnail

👀 1. 들어가며

1.1. 상황

업무에서 위경도 데이터를 다룰 일이 많아져, 지도 시각화를 위해 python의 folium 라이브러리를 스터디하고 있습니다. Python folium을 사용해 대한민국 법정동 경계선을 시각화한 경험을 공유합니다

꿀팁) 법정동과 행정동의 차이

  • 법정동 : 법률로 지정된 행정구역 단위. 1914년 시행된 행정구역 통폐합 때 정해져 대부분 현재까지 유지 중으로 등기부등본 등에 사용
  • 행정동 : 현실적인 문제를 고려해 만들어진 단위로 주민센터 설치 기준으로 사용

하나의 법정동이어도 주민센터가 여러 개라면 행정동이 여러 개인 것이고, 여러 곳의 법정동 중 단 한 곳에만 주민센터가 있다면 행정동이 하나입니다. 법정동은 거의 변동이 없고, 행정동은 행정 운영에 따라 수시로 설치 또는 폐지됩니다. 변동이 많은 행정동 기준으로 보면, 번거롭기 때문에 법정동 기준으로 데이터를 보고 있어서 법정동으로 경계선 그리는 방법을 소개합니다. 저의 경험이 담긴 🍯꿀팁들도 함께 담아봤어요!

1.2. 예상 독자

  • 대한민국 위도/경도 데이터를 활용하여 법정동 경계선이 그려진 지도에 위치를 찍어보는 등 시각화를 해보고 싶은 분들
  • Python (pandas 등)을 활용한 데이터 처리에는 경험이 있으나, Folium을 활용하여 지리적 데이터를 시각화하는 것은 처음 시도하는 분들

🗺️ 2. Folium으로 법정동 경계선 시각화하기

2.1. Folium 소개

Folium은 지리공간 데이터를 시각화하는 데 사용되는 Python 라이브러리입니다! 유용한 시각화 방법이 많은데, 대표적인 몇 가지만 소개해드릴게요. 더 자세히 살펴보고 싶으신 분들은 아래 링크를 통해 다양항 예시를 학습 해보시면 좋을 것 같아요

2.1.1. folium 설치

folium을 설치해주세요. 설치 후, 모듈을 import해서사용하면 됩니다.

pip install folium

import folium

2.1.2. 지도 생성

m = folium.Map(location=(37.51434733724219, 127.07303593988632), tiles="OpenStreetMap", zoom_start=16)`
m
  • location : 기준점이 될 위경도 좌표 입력
  • tiles : 배경지도 타일 형식 지정
  • zoom_start : 줌 크기 설정

꿀팁) 다양한 배경지도 타일 형식
folium은 배경지도로, OpenStreetMap에서 제공하는 오픈 소스 지도를 사용합니다. 다양한 타일 형식이 python folium 공식 활용 가이드 중 타일 형식 설명 파트 에 잘 정리되어있으니 원하는 형식을 찾아서 사용해보세요!

저는 location에 잠실종합운동장의 위경도 좌표를 입력해줘서 아래와 같은 지도를 출력해봤습니다.

꿀팁) 위경도 좌표를 쉽게 확인 할 수 있는 방법

  • 위경도 확인 사이트 활용
    회사 동료가 추천해줘서 위 사이트를 이용해 지역명이나 주소를 검색 해서 위도,경도를 쉽게 찾아서 사용하고 있어요. 접속이 안될 때도 있어서 안되면 아래 방법을 활용해보세요.
  • ⓑ folium으로 확인
    m.add_child(folium.LatLngPopup())
    위 코드를 folium지도 생성할 때 함께 작성해주세요. 지도를 클릭하면 위경도를 쉽게 확인할 수 있습니다.!
  • ⓒ 구글지도에서 확인
    구글지도에서 파악할 수 있는 방법을 공유합니다. 구글지도에서 원하는 장소를 검색 → 마커 주변에 마우스 커서 → 오른쪽 클릭 → 이곳이 궁금한가를 클릭→ 회색 지도 아이콘을 클릭→ 위도, 경도 파악가능!!

2.1.3. Marker와 popup 활용

지도 위에 마커를 표시해줄 수 있습니다.

folium.Marker(
	location=[37.5161, 127.0760],
	tooltip="이곳의 이름은?",
	popup="<b>잠실체육관</b>",
	icon=folium.Icon(color="green"),
).add_to(m)
m
  • location : 마커를 찍을 위경도 입력
  • tooltip : 마우스 커서를 마커에 가져다대면 보이는 설명이나 정보를 입력 (사용자에게 마커에 대한 정보 제공 가능)
  • popup : 마커를 클릭하면 표시할 상세 정보 입력, 텍스트 뿐만 아니라 이미지나 웹페이지 등 입력 가능
  • icon : 표시하는 아이콘은 모양이나 색깔을 설정

꿀팁) 다양한 icon 종류 활용하기
icon 종류는 *glyphicons 에서 찾아보세요, icon = “작성”에 입력해주면 됩니다.

ⓐ 올림픽 주경기장을 빨간색 마커로 표시하고 '올림픽 주경기장'이라는 팝업을 띄워봤어요.

folium.Marker(
    location=[37.51587923093469,127.07273202617604],
    tooltip="이곳의 이름은?",
    popup="<b>올림픽주경기장</b>",
    icon=folium.Icon(color="red"),
).add_to(m)
m

ⓑ 팝업 크기를 조정해서 가독성을 더 좋게 해봅시다!

popup_content = "올림픽주경기장"
popup = folium.Popup(popup_content, max_width=300)

folium.Marker(
  location=[37.51587923093469,127.07273202617604],
  tooltip="이곳의 이름은?",
  popup=popup,
  icon=folium.Icon(color="red"),
).add_to(m)
m

ⓒ 잠실야구장에는 팝업으로 ⚾2023년 한국시리즈 우승팀🏆이 누군지 담긴 기사를 띄워볼게요!

folium.Marker(
  location=[37.51216756170485,127.07197060104319],
  tooltip="2023년 한국시리즈 우승은?",
  popup='<iframe width="500" height="400" src="https://imnews.imbc.com/replay/2023/nwdesk/article/6543091_36199.html" frameborder="0"></iframe>',
  icon=folium.Icon(icon="heart"),
).add_to(m)
m

ⓓ 2023년 한국시리즈에 올라온 두 팀의 ⚾KBO 정규리그 결과를 데이터프레임으로 만들어 표시해줄게요!

import pandas as pd

df = pd.DataFrame(
  data=[["86승", "79승"], ["56패", "62패"], ["2무", "3무"]],
  columns=["LG", "KT"],
  index=['승', '패', '무']
)

#df.to_html()을 사용하여 데이터프레임을 HTML 형식의 테이블로 변환
popup_content = folium.Popup(df.to_html(classes="table table-striped table-hover table-condensed"), max_width=300)

folium.Marker(
  [37.51216756170485, 127.07197060104319]
  ,popup=popup_content
).add_to(m)
m

2.1.4. 선, 도형 활용

지도에 다양한 요소들을 그려볼게요.

ⓐ 선 그리기
종합운동장역 → 잠실야구장 → 올림픽 주경기장을 선으로 이어볼게요 각 위치의 위경도 좌표를 작성해주고 folium.PolyLine을 사용해서 그려주면 됩니다!

line_coords = [
  (37.51108512059325,127.07397154356342),
  (37.51216756170485,127.07197060104319),
  (37.51587923093469,127.07273202617604)
] 

folium.PolyLine(
  locations=line_coords, # 선의 위경도 좌표
  color='blue',  # 선 색상
  weight=5,  # 선 굵기
  opacity=0.7,  # 선 투명도
  tooltip="종합운동장역에서 잠실야구장 찍고, 올림픽주경기장으로" 
).add_to(m)
m

ⓑ 원 그리기
잠실새내수영장을 원으로 표시해줄게요 folium.CircleMarker를 사용해서 그려주면 됩니다!

popup_content = "잠실새내수영장"
popup = folium.Popup(popup_content, max_width=300)

folium.CircleMarker(
  location=[37.514507502409714, 127.0762258398054], #위경도 좌표
  radius=15, # 원 크기
  fill_color="yellow",# 채우기 색상
  fill_opacity=0.6, # 채우기 투명도
  color="orange", # 테두리 색상
  weight=3, # 테두리 두께
  opacity=1, # 테두리 투명도
  popup=popup
).add_to(m)
m

ⓒ 사각형 그리기
folium.Rectangle를 사용해서 사각형을 그려줄게요.

rectangle_coords = [[37.5174,127.0703], [37.5121,127.0779]]

folium.Rectangle(
  bounds=rectangle_coords, #사각형 좌표 설정 (사각형의 모서리 좌표 입력)
  fill_color='skyblue',  # 채우기 색상
  fill_opacity=0.4,  # 채우기 투명도
  color='blue',  # 테두리 색상
  weight=2,  # 테두리 두께
  opacity=0.7  # 투명도
).add_to(m)
m

ⓓ 다각형 그리기
folium.Polygon를 사용해서 다각형을 그려줄게요.

locations = [
[37.51108512059325,127.07397154356342],
[37.51216756170485,127.07197060104319],
[37.51587923093469,127.07273202617604]
]

popup_content = "종합운동장-잠실야구장-올림픽주경기장"
popup = folium.Popup(popup_content, max_width=300)

folium.Polygon(
  locations=locations, # 다각형 좌표 입력
  fill_color="skyblue", # 채우기 색상
  fill_opacity=0.5, # 채우기 투명도
  color="blue", # 테두리 색상
  weight=3, # 테두리 두께
  opacity=1, # 테두리 투명도
  popup=popup
).add_to(m)
m

2.1.5. layer control과 FeatureGroup 활용

  • LayerControl를 사용하면 지도의 여러 정보를 제어하는 컨트롤을 생성해볼 수 있어요. 지도에 표시한 여러 정보를 쉽게 끄고 켜고 할 수 있어요!
  • FeatureGroup을 활용하면, 마커 등을 그룹화해서, layercontrol에서 유용하게 활용할 수 있어요!
# 잠실야구장 Group 생성
group_1 = folium.FeatureGroup("잠실야구장").add_to(m)
folium.Marker((37.51216756170485,127.07197060104319), icon=folium.Icon(icon="heart")).add_to(group_1)

# 올림픽 주경기장 Group 생성
group_2 = folium.FeatureGroup("올림픽주경기장").add_to(m)
folium.Marker((37.51587923093469,127.07273202617604), icon=folium.Icon("green")).add_to(group_2)

#LayerControl생성 (collapsed=False를 해두면 항상 펼쳐둘 수 있고, 기본값은 True)
folium.LayerControl(collapsed=False).add_to(m)

2.2. 법정동 경계선 시각화

꿀팁) GeoJSON파일 바로 활용하기
2.2.1 ~ 2.2.3은 folium에서 사용가능한 GeoJSON 데이터로 변환하는 과정을 담고있어요! 만들어진 GeoJSON데이터를 바로 사용하고 싶으시면 git-hub에 올려뒀으니 다운받아서 사용하세요! 다운 받으신 후에, 2.2.4가 법정동 경계선을 시각화하는 부분이니 해당 목차로 바로 넘어가시면 됩니다.

2.2.1. 대한민국 최신 행정구역(SHP)다운로드

특정구(ex. 강남구, 송파구 등)의 법정동 경계선을 그리기위해, 시군구와 읍면동 데이터를 대한민국 최신 행정구역(SHP)파일을 제공하는 지오서비스 블로그에서 가장 최신버전으로 다운로드 받아주세요. 지오서비스에서 대한민국의 행정구역에 대한 시도, 시군구, 읍면동(법정동), 에 대한 공간 데이터를 쉽게 사용할 수 있도록 병합하여 제공해주고 있어요. 무료로 공개해주고 계셔서 지오서비스의 대한민국 최신 행정구역(SHP) 다운로드해서 활용했습니다. (지오서비스 정말 감사해요!)

2.2.2. 지도 좌표계 WGS84로 변환

다운받은 파일의 지도 좌표계는 UTM-K로 되어 있어, 위도/경도 형태의 WGS84로 변경해줘야 합니다. UTM-k(Universal Transverse Mercator - Kore)는 한국 지역에 특화된 좌표계이며, WGS84는 전 세계에서 사용되는 표준적인 지구 좌표계인데요. WGS84 좌표계는 위도(latitude)와 경도(longitude)로 표현되어있어요. UTM-k는 Folium에서 사용하기 어렵기에, 위경도 형태의 WGS84로 변환해줘야합니다.

  • ⓑ 좌표계(UTM-K → WGS84)로 변환
    (2.2.1.)에서 다운받은 파일 압축 풀기→ 설치한 프로그램 열기 → 폴더 대상 shp파일 변환 탭 선택 → 입력 폴더에 압축을 푼 폴더 지정 후 좌표계 UTM-K(GRS80 타원체) 선택 → 출력폴더 지정 후 좌표계 WGS84타원체의 경위도 선택 한뒤 변환을 눌러주면 됩니다. (시군구와 읍면동 두개의 폴더를 다운받았으니, 이 과정을 두번 진행해주시면 됩니다!)

2.2.3. GeoJSON형태로 변환

folium에서 사용할 수 있는 GeoJSON형태로 바꿔줄게요!(시군구와 읍면동리 두개의 폴더를 변환했으니, 이 과정을 두번 진행해주시면 됩니다!)

  • ⓑ 3개 파일을 선택 →encoding=euckr 입력 →import 클릭
  • ⓒ 오른쪽 상단 Expprt 버튼 클릭 → (File format = GeoJSON 선택 encoding=utf-8 입력 → Export 클릭 → GeoJSON파일 생성

2.2.4. folium활용해서 시각화

송파구, 강남구, 강동구의 법정동을 시각화해볼게요!


# 설치 안되어 있는 경우 설치
#pip install geopandas pandas

import geopandas as gpd
import pandas as pd
import folium

#파일 경로 지정
path = '경로 작성'

# (1) 시군구/geojson 불러오기
df_gu= gpd.read_file(path + 'sig.json')

# (2) 읍면동/geojson 불러오기
df_bdong = gpd.read_file(path + 'emd.json')

# (3) merge 해주기 (특정구의 법정동을 시각화할 것이기 때문)
result_df = pd.merge(df_gu, df_bdong, left_on=df_gu['SIG_CD'], right_on=df_bdong['EMD_CD'].str[:5], how='left')
result_df


# (4) 송파구, 강동구, 강남구 지정 
gdf_test = result_df[result_df['SIG_KOR_NM'].isin(['송파구', '강동구', '강남구'])].reset_index(drop=True)

# (5) 구 별 법정동 시각화
# 지도 생성
m = folium.Map(location=(37.51434733724219, 127.07303593988632), tiles="OpenStreetMap", zoom_start=12)

# 시각화 할 경계선 색상 지정
colors = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 'beige', 'darkblue', 'darkgreen']

# FeatureGroup 사전 생성
layer_dict = {}

# 각 경계선을 지도에 추가
for idx, row in gdf_test.iterrows():
    
    gu_name = row['SIG_KOR_NM']
    dong_name = row['EMD_KOR_NM']
    polygon_wkt = row['geometry_y']
    
    if gu_name in layer_dict:
        layer = layer_dict[gu_name]
    else:
        # 해당 구이름에 대한 FeatureGroup가 없으면 새로 생성
        layer = folium.FeatureGroup(name=gu_name,show=False)
        layer_dict[gu_name] = layer
        layer.add_to(m)
    
    color = colors[len(layer_dict) % len(colors)]
    
    t1 = folium.GeoJson(polygon_wkt, 
                       style_function=lambda feature, color=color: {
                           'fillColor': color, #채우기 색상
                           'fillOpacity': 0.1, #채우기 투명도
                           'color': color, # 테두리 색상
                           'weight': 1, #테두리 두께
                           'opacity': 1  # 테두리 투명도
                       },
                       tooltip=f'법정동: {dong_name}')
    
    t1.add_to(layer)
    
# folium.LayerControl 추가한 뒤, 지도 표시
folium.LayerControl(collapsed=False).add_to(m)
folium.LatLngPopup().add_to(m)
display(m)

# html파일로 저장
m.save("법정동시각화_231210.html")

송파구, 강남구, 강동구의 법정동 경계선을 시각화했습니다. 각 법정동에 마우스 커서를 가져다대면 법정동 이름을 확인할 수 있고, 오른쪽 상단 LayerControl을 활용해서 선택해서 볼 수도 있어요!


🙌 3. 정리하며

Python Folium을 사용해서 대한민국 법정동 경계선을 시각화한 경험을 소개했는데요. Folium실습 코드, Geojson파일, 법정동 경계선 그리는 코드는 git-hub에 업로드해뒀으니, 참고해주세요!

저는 법정동 단위로 경계선을 시각화하는 것을 소개해 드렸는데, 보유하신 데이터의 지리적인 특징을 고려해, 도/시/군/구별 데이터를 활용해서 특정 지역의 경계선을 시각화 해보셔도 좋을 것 같아요. 지도 데이터는 처음엔 어려워 보이는데 다룰수록 재밌고 해볼 수 있는 게 많습니다! 시도해보시길 추천 드립니다. 저도 업무에 활용하면서 유용한 기능이 있으면 글로 또 다뤄볼게요! 감사합니다🥳


📑 참고 자료

profile
말보다는 행동, 일단 해보고 있는 Business Analyst입니다. 🌠시리즈 탭을 클릭하시면 분류 별로 글을 보실 수 있습니다!

0개의 댓글