본문 바로가기

코딩

대한민국 법정구역 SHP 파일을 GeoJSON으로 변환하기

튜토리얼 없이 GeoJSON파일만 필요한 분들은 아래 링크로 다운받으시면 됩니다.

https://drive.google.com/file/d/1VkVQJcyEUq2KW9E9mOUeSDIoiZibVcaG/view?usp=sharing 

 

법정구역 GeoJSON 데이터_23년8월.zip

 

drive.google.com

 

튜토리얼을 위한 행정구역 SHP 파일은 아래 링크에서 신청하여 다운받을 수 있습니다.

https://business.juso.go.kr/addrlink/main.do

 

주소기반산업지원서비스

본인인증 사용중인 휴대전화번호로 인증 인증하기 아이핀 인증 본인 명의 아이핀 계정으로 인증 인증하기

business.juso.go.kr

주소정보제공 → 제공하는 주소  → 구역의 도형  → 원하는 월을 선택하여 신청
신청이 승인된 모습

주소기반산업지원서비스에서 제공하는 파일은 시도별(서울특별시, 부산광역시 등)로 파일이 나뉘어있는데 아래 블로그에서는 별도 신청 없이 하나로 통합된 파일을 받을 수 있습니다. (감사하게도)

http://www.gisdeveloper.co.kr/?p=2332 

 

대한민국 최신 행정구역(SHP) 다운로드 – GIS Developer

 

www.gisdeveloper.co.kr

본 글에서는 주소기반산업지원서비스에서 제공한 파일을 사용하여 GeoJSON 파일을 만드는 방법을 알아보겠습니다.

시도별 폴더

먼저 다운받은 압축파일을 한곳에 풀면 위와 같이 시도별 폴더가 생성됩니다.

각 폴더 안에는 위와 같은 파일들이 들어있는데요, 각 파일별 내용은 다음과 같습니다.

  • TL_SCCO_CTPRVN : 법정구역_시도
  • TL_SCCO_SIG : 법정구역_시군구
  • TL_SCCO_EMD : 법정구역_읍면동
  • TL_SCCO_LI : 법정구역_리
  • TL_SCCO_GEMD : 행정구역
  • TL_KODIS_BAS : 기초구역
  • TL_SPPN_MAKAREA : 지점번호표기 의무지역

오늘 글에서는 시군구 파일을 예시로 활용하겠습니다.

 

1. 필요 패키지

  • pandas
  • geopandas

 

2. 시도별 shp 파일을 하나로 합쳐서 GeoJSON파일로 내보내기

import os
import geopandas
import pandas as pd

cwd = os.getcwd()

folders = [x for x in os.listdir(cwd) if not os.path.isfile(x)]
# 같은 경로에 있는 모든 폴더명을 리스트로 반환합니다.

shp_files =[f"{x}/TL_SCCO_SIG.shp" for x in folders]
# 각 폴더에 있는 TL_SCCO_SIG.shp 파일을 리스트로 반환합니다.

geodf = geopandas.GeoDataFrame()
# 비어있는 geopandas dataframe을 만들어줍니다.

for file in shp_files:
    temp = geopandas.read_file(file, encoding='cp949')
    # shp_files 리스트에 있는 파일을 하나하나 읽어옵니다.
    geodf = pd.concat([geodf,temp], sort=False).reset_index(drop=True)
    # 읽어온 파일과 geodf를 합쳐줍니다.
    
geodf.to_file('법정구역_시군구.geojson', driver='GeoJSON')
# geopandas dataframe을 geojson 파일로 저장합니다.

만들어진 GeoJSON파일을 https://mapshaper.org/ 에서 확인해보면 다음과 같습니다.

 

3. 좌표계 설정하기

위에서 얻어진 GeoJSON 데이터를 바로 활용하려고 하면 한가지 문제를 겪게됩니다. GeoJSON 데이터에 포함된 좌표정보가 통상 사용되는 위도와 경도를 이용한 좌표가 아닌 한국에서 주로 쓰는 좌표계로 이루어져있기 때문인데요,

먼저 GeoPandas DataFrame geometry열의 좌표계 (Coordinate Reference System, CRS)가 EPSG:5179(한반도 기준)임을 셋팅해주고 좌표계를 EPSG:4326(전세계 기준)으로 변환해줍니다.

※ UTM-K (GRS80) 좌표계를 사용하는 경우는 아래 코드를 생략하시면 됩니다.

geodf = geodf.set_crs(epsg=5179)
geodf = geodf.to_crs(epsg=4326)

geodf.to_file('법정구역_시군구.geojson', driver='GeoJSON')

 

4. 작은 섬들을 지우고 지도를 단순화하기

이미 필요한 GeoJson 데이터를 생성하였지만 한가지 문제가 더 남아있습니다. 한국의 해안선은 작은 섬들이 너무 많아 복잡하다는 점인데요, 이때문에 GeoJson 데이터의 크기도 필요이상으로 커지게 됩니다. 따라서 너무 작은 구역들은 삭제하는 방법을 통해 지도를 단순화 시켜보도록 하겠습니다.

from shapely.geometry import MultiPolygon

def filter_small_polygons(multi_polygon, threshold_area):
    filtered_polygons = []
    if type(multi_polygon)==MultiPolygon:
        for polygon in list(multi_polygon.geoms):
            if polygon.area >= threshold_area:
                filtered_polygons.append(polygon)
        
        return MultiPolygon(filtered_polygons)
    else:
        return multi_polygon

위 함수는 여러개의 도형 (MultiPolygon) 중에서 특정 넓이 기준 (threshold_area)에 못미치는 도형을 삭제하는 함수입니다.

함수를 이해하기 전에 먼저 각 시군구의 모양은 하나 또는 여러개의 도형으로 이루어져있다는 사실을 알아야 하는데요, 예를들어 서울시 종로구는 하나의 큰 도형으로 나타낼 수 있지만 전남 영광군은 60개의 도형으로 나타내집니다. 자잘한 섬들까지 포함하고 있기 때문입니다. 따라서 위 함수를 적용하여 넓이가 작은 도형(섬)들을 지워주도록 하겠습니다.

 

그리고 꼬불꼬불한 테두리를 일자로 살짝 펴주는 함수도 함께 적용하겠습니다.

geodf['geometry'] = geodf['geometry'].apply(lambda x: filter_small_polygons(x, threshold_area=7000000))
# threshold를 더 크게 설정할 경우 지워지는 구역이 생깁니다.

geodf['geometry'] = geodf['geometry'].simplify(100)
# simplyfy 함수는 꼬불꼬불한 테두리를 일자로 펴줍니다.

geodf = geodf.set_crs(epsg=5179)
geodf = geodf.to_crs(epsg=4326)
# 좌표계를 바꾸는 것은 섬을 지운 이후에 적용해줍니다.

geodf.to_file('법정구역_시군구.geojson', driver='GeoJSON')

깔끔해진 해안선

전체 코드

import os
import geopandas
import pandas as pd

cwd = os.getcwd()

folders = [x for x in os.listdir(cwd) if not os.path.isfile(x)]
# 같은 경로에 있는 모든 폴더명을 리스트로 반환합니다.

shp_files =[f"{x}/TL_SCCO_SIG.shp" for x in folders]
# 각 폴더에 있는 TL_SCCO_SIG.shp 파일을 리스트로 반환합니다.

geodf = geopandas.GeoDataFrame()
# 비어있는 geopandas dataframe을 만들어줍니다.

for file in shp_files:
    temp = geopandas.read_file(file, encoding='cp949')
    # shp_files 리스트에 있는 파일을 하나하나 읽어옵니다.
    geodf = pd.concat([geodf,temp], sort=False).reset_index(drop=True)
    # 읽어온 파일과 geodf를 합쳐줍니다.

from shapely.geometry import MultiPolygon

def filter_small_polygons(multi_polygon, threshold_area):
    filtered_polygons = []
    if type(multi_polygon)==MultiPolygon:
        for polygon in list(multi_polygon.geoms):
            if polygon.area >= threshold_area:
                filtered_polygons.append(polygon)
        
        return MultiPolygon(filtered_polygons)
    else:
        return multi_polygon
    
geodf['geometry'] = geodf['geometry'].apply(lambda x: filter_small_polygons(x, threshold_area=30000000))
geodf['geometry'] = geodf['geometry'].simplify(100)


geodf = geodf.set_crs(epsg=5179)
geodf = geodf.to_crs(epsg=4326)


geodf.to_file('법정구역_시군구_simplified.geojson', driver='GeoJSON')

 

이렇게 만들어본 GeoJSON파일을 활용하여 다음 글에서는 Choropleth Map을 그리는 방법을 소개해보겠습니다.

감사합니다.