지방행정 인허가 데이터개방: www.localdata.go.kr/
LOCALDATA - 지방행정인허가데이터개방
지방행정 인허가 데이터개방 의료기관, 의료기기 데이터 보기 동물, 축산 데이터 보기 게임, 공연, 관광, 문화기획, 노래방, 비디오, 숙박, 여행, 영화, 음악 데이터 보기 미용, 이용, 세탁소/빨래
www.localdata.go.kr
서울 데이터에 한하여 다음의 링크에서 다운로드 가능: seoul_localdata.json Dropbox
(다른 지역은 아래의 코드 참조하여 정제하여 사용)
해당 웹페이지에서 전체 데이터, 업종별 데이터, 혹은 지역별 데이터를 다운로드 할 수 있다. 예를들어 서울시의 데이터를 다운로드 해보자.
XML, EXCEL, CSV의 포맷을 제공하지만 CSV나 EXCEL은 버전이나 파일의 인코딩 등에 의해 깨지는 경우가 있었다. 따라서 XML로 다운로드 하는것을 추천한다. 그러면 6110000_XML.zip과 같은 이름의 파일이 다운로드 될 것이다. 이를 zip으로 압축풀거나, 리눅스 환경에서는
unzip -I EUC-KR 6110000_XML.zip -d seouldata_XML
다음의 명령어로 EUC-KR의 인코딩을 지켜가며 seouldata_XML이라는 경로에 데이터의 압축을 푼다.
각 XML 파일을 Python에서 불러오는 과정에서는 각각의 파일의 크기가 크기때문에 기본적인 xml라이브러리보다는 lxml이라는 라이브러리를 사용하는것이 편하다. 설치는 pip install lxml 을 통해 가능하다.
import os
import json
from tqdm import notebook
# seouldata_json 경로가 없는 경우 새롭게 만든다
if not os.path.isdir('seouldata_json'):
os.mkdir('seouldata_json')
# xml파일을 하나씩 읽어 json으로 변환하여 저장한다
for fname in notebook.tqdm(os.listdir('seouldata_XML')):
doc = etree.parse('seouldata_XML/' + fname)
root = doc.getroot()
header = root[0]
mpage = {r.tag:int(r.text) for r in header[1]}
if mpage['totalCount'] == 0:
continue
body = root[1]
mdata = [{elem.tag: elem.text for elem in row} for row in body[0]]
assert mpage['totalCount'] == len(mdata)
with open('seouldata_json/' + fname[:-3] + 'json', 'w') as fp:
json.dump(mdata, fp)
XML파일에서 header 부분은 column과 paging으로 구성되어 있는데 column은 컬럼의 이름과 설명 (예: opnSvcNm는 개방서비스명 등) 과 paging은 파일내의 totalCount 를 가지고 있다. 위의 코드대로 totalCount가 0일 경우에는 body에 내용이 없기 때문에 위와 같이 데이터를 json의 형태로 변환하여 저장한다. 이후 pandas 등으로 데이터를 활용하면 된다.
참고로 위 데이터는 각 파일마다 조금씩 데이터 컬럼이 다르다. 공통된 컬럼만을 추출하여 데이터를 구축하고 싶다면 아래의 방법을 따르면 된다.
data_cols = []
for fname in notebook.tqdm(os.listdir('seouldata_json')):
if fname[-4:] == 'json':
with open('seouldata_json/' + fname) as fp:
json_data = json.load(fp)
data_cols.append(list(json_data[0].keys()))
먼저 각 json 파일의 첫번째 아이템의 keys 값들만을 data_cols의 리스트에 추가한다.
kcols = data_cols[0]
for cols in data_cols[1:]:
nkcols = []
for c in cols:
if c in kcols:
nkcols.append(c)
kcols = nkcols
이후 data_cols[0]를 kcols값으로 두어 다른 파일에 있는 cols에서도 공통적으로 존재할 경우에만 해당 column을 남기는 식으로 하여 공통된 컬럼리스트인 kcols 를 구한다.
all_data = []
for fname in notebook.tqdm(os.listdir('seouldata_json')):
if fname[-4:] == 'json':
with open('seouldata_json/' + fname) as fp:
json_data = json.load(fp)
all_data.extend([{k:item[k] for k in kcols} for item in json_data])
마지막으로 all_data에 각 데이터마다 kcols에 해당하는 값만을 아이템으로 추가하여 데이터 리스트를 만들고,
import pandas as pd
df = pd.DataFrame(all_data)
이를 통해 전체 데이터를 df라는 데이터 프레임으로 만든다.
마지막으로 위 pandas 데이터를 geopandas 데이터로 변환하여 사용하고 싶다면 df['x']와 df['y']를 사용하면 되는데, 이 과정에서 약간의 데이터 정제가 필요하다.
df['x']와 df['y']의 경우 데이터가 비어있는 경우 (None)가 있기때문에 아래의 명령어로 None이 아닌 데이터를 필터링한다.
df = df[(df['x'] == df['x']) & (df['y'] == df['y'])].copy()
이후 df['x']값을 보면 값이 object 타입인 것을 알 수 있다. 이를 float 타입으로 변환시킨다.
df['x'] = df['x'].astype(float)
df['y'] = df['y'].astype(float)
그리고 다음과 같이 df.x와 df.y값을 이용하여 gdf라는 GeoDataFrame을 만들수가 있다.
import geopandas as gpd
gdf = gpd.GeoDataFrame(
df, geometry=gpd.points_from_xy(x=df.x, y=df.y)
)
gdf.crs = "+proj=tmerc +lat_0=38 +lon_0=127.0028902777778 +k=1 +x_0=200000 +y_0=500000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43"
ngdf = gdf.to_crs('EPSG:4326')
이때 localdata.go.kr의 데이터는 다음의 TM 좌표계를 사용한다.
+proj=tmerc +lat_0=38 +lon_0=127.0028902777778 +k=1 +x_0=200000 +y_0=500000 +ellps=bessel +units=m +no_defs +towgs84=-115.80,474.99,674.11,1.16,-2.31,-1.63,6.43
이를 이용하여 crs를 설정하고, ngdf라고 하는 EPSG:4326으로 변환된 (위도 경도 좌표계) 데이터로 시각화가 가능하다.
from cartoframes.viz import Layer, color_category_style
Layer(ngdf.sample(frac=1).iloc[:2000], color_category_style('opnSvcNm'))
내가 주로 활용하는 CartoFrames의 예제를 참고하여 업종별로 지도상에 2000개의 샘플만 표현한 결과이다.
import os, zipfile
save_gdf = ngdf.copy()
save_gdf.crs = 'EPSG:4326'
save_dir = 'seouldata_gis'
save_name = 'seouldata_gis'
if not os.path.isdir(save_dir):
os.mkdir(save_dir)
save_gdf.to_file(save_dir + '/' + save_name + '.shp', encoding='euc-kr')
fantasy_zip = zipfile.ZipFile(save_dir + '/' + save_name + '.zip', 'w')
for ext in ['cpg', 'dbf', 'shp', 'shx', 'prj']:
fantasy_zip.write(save_dir + '/' + save_name + '.' + ext, save_name + '.' + ext, compress_type = zipfile.ZIP_DEFLATED)
fantasy_zip.close()
최종적으로 shp 파일의 형태로 저장하고 싶으면 다음과 같이 저장하면된다. 그러나 encoding이나 컬럼이름의 길이 등에 의해 데이터가 손실될 수 있다. 또한 shape파일을 일일히 저장하면 데이터 용량이 너무 커지기도 한다. 이 경우에
ngdf['lng'] = ngdf.geometry.x
ngdf['lat'] = ngdf.geometry.y
ncols = list(ngdf.columns)
ncols.remove('geometry')
ngdf[ncols].to_json('seoul_localdata.json')
다음과 같이 geometry를 제외한 나머지 컬럼들을 json의 형태로 저장하여 이후 필요할때 활용하면 된다.
전체 코드는 github.com/SuminHan/localdata.go.kr 에 정리되어 있다.
'GIS Tech > GIS Data Process' 카테고리의 다른 글
Python (GIS+General) Tip 모음 (5) | 2021.01.11 |
---|---|
주요 좌표계 활용 방법 (TM, UTM-K, KATEC) (0) | 2021.01.11 |
지도상 영역의 GeoJSON Polygon 생성 웹앱 만들기 (Python) (0) | 2021.01.10 |
WMS 를 이용하여 TOPIS 교통 링크 지도데이터 얻기 (2) | 2021.01.08 |
Instagram Crawling by shortcode (0) | 2021.01.08 |
댓글