R) ggplot2 - 한국 행정경계지도

R) ggplot2 - 한국 행정경계지도

shp 파일을 활용하여 한국 지도를 시각화 하는 방법을 알아본다.

개요

지도를 시각화 하기 위해서는 일반적으로 shp파일(“shape 파일”로 읽으면 된다.)을 사용하는 경우가 많다. 물론 rnaturalearth 패키지나 rworldmap 같은 지도 관련 패키지도 있지만 해당 패키지는 한국의 시도 또는 읍면동 구분 까지 상세한 정보를 제공하지 않는다.
패키지 기반 지도 시각화 보러가기

참고로 국토교통부의 국가공간정보포털에서 제공하는 데이터는 오류가 많아서 실무에 못쓴다는 말이 많다. 많은 분들이 민원을 넣어 바로잡을 수 있도록 도와주셨으면 한다.

지도 데이터 확보

지도 데이터는 GIS DEVELOPER에서 제공하는 자료를 사용하며 정확하게는 해당 웹페이지의 대한민국 최신 행정구역(SHP) 다운로드 게시물이다. 해당 게시물에는 시도/시군구/읍면동/리 이렇게 네 종류의 지도 데이터를 다운로드 할 수 있도록 조치해놨다.
지도 데이터 종류

본 포스팅 작성 시점인 2021년 1월 버전을 다운받으면 다음과 같이 확인할 수 있다.
지도 데이터 압축파일 확인

지도 데이터 확인

각 압축파일에는 4개의 파일이 있는데 그 중 shp 확장자를 타겟으로 rgdal 패키지의 readOGR() 함수를 사용해야 한다.

1
2
3
4
5
6
7
8
library("rgdal")
map = readOGR("TL_SCCO_CTPRVN.shp")
class(map)
## [1] "SpatialPolygonsDataFrame"
## attr(,"package")
## [1] "sp"
slotNames(map)
## [1] "data" "polygons" "plotOrder" "bbox" "proj4string"

shp 파일을 읽어온 객체의 클래스는 SpatialPolygonsDataFrame 으로 S4 클래스이다. 그렇게 때문에 slotNames() 함수로 상태를 파악하면 상기와 같이 다섯 개의 슬롯을 확인할 수 있다. 그 중에서 “data”슬롯에는 다음과 같은 정보가 있다.

1
2
3
4
5
6
7
8
9
df_map_info = map@data
head(df_map_info)
## CTPRVN_CD CTP_ENG_NM CTP_KOR_NM
## 0 42 Gangwon-do 강원도
## 1 41 Gyeonggi-do 경기도
## 2 48 Gyeongsangnam-do 경상남도
## 3 47 Gyeongsangbuk-do 경상북도
## 4 29 Gwangju 광주광역시
## 5 27 Daegu 대구광역시

각 변수별 설명은 다음과 같다.
● CTPRVN_CD: 행정구역 코드
● CTP_ENG_NM: 행정구역 영문명
● CTP_KOR_NM: 행정구역 국문명

참고로 행정구역 코드는 행정안전부의 행정표준관리시스템의 법정동 코드를 준수하며 code.go.kr에서 확인할 수 있다. 법정동 코드는 총 10자리 숫자로 구성되어 있으며 현재 확인한 데이터는 시도 구분 데이터이기 때문에 첫 두 자리의 코드가 기록되어있다.

시각화

시각화는 ggplot2 패키지를 기반으로 진행할 예정이기 때문에 미리 불러오도록 하자.

1
library("ggplot2")

시도

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
map = readOGR("TL_SCCO_CTPRVN.shp")
df_map = fortify(map)
head(df_map)
## long lat order hole piece id group
## 1 1091705 2034023 1 FALSE 1 0 0.1
## 2 1091705 2034038 2 FALSE 1 0 0.1
## 3 1091656 2034038 3 FALSE 1 0 0.1
## 4 1091616 2034059 4 FALSE 1 0 0.1
## 5 1091570 2034089 5 FALSE 1 0 0.1
## 6 1091550 2034094 6 FALSE 1 0 0.1

ggplot(data = df_map,
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
theme(legend.position = "none")

행정경계지도 - 시도

그런데 각각의 행정구역을 따로 그리려면 어떻게 해야할까? 이 질문의 해답은 “df_map” 객체의 “id” 변수에 있다. 다음과 같이 코드를 작성하고 결과를 확인해보자.

1
2
3
4
5
ggplot(data = df_map[df_map$id == 0, ],
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
theme(legend.position = "none")

행정경계지도 - 시도, id == 0

id 0번은 강원도로 추정된다. 그렇다면 다음 코드도 확인해보자.

1
2
3
4
5
6
7
ggplot(data = df_map[df_map$id %in% 0:5, ],
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
facet_wrap(~ id, nrow = 2,
scales = "free") +
theme(legend.position = "none")

행정경계지도 - 시도, id 0 ~ 5

시도 개수가 많기 때문에 여섯 개만 추려서 그린 결과는 위와 같다. 문제는 각 행정구역을 id값 바꿔가면서 확인하기는 참 번거로운데 이 문제를 어떻게 해결해야 할까? 사실 앞에 데이터를 살펴보면서 확인한 객체에 단서가 있다.

1
2
3
4
5
6
7
8
9
df_map_info = map@data
head(df_map_info)
## CTPRVN_CD CTP_ENG_NM CTP_KOR_NM
## 0 42 Gangwon-do 강원도
## 1 41 Gyeonggi-do 경기도
## 2 48 Gyeongsangnam-do 경상남도
## 3 47 Gyeongsangbuk-do 경상북도
## 4 29 Gwangju 광주광역시
## 5 27 Daegu 대구광역시

“df_map_info” 객체의 rowname과 id가 같기 때문에 다음과 같이 처리해준다.

1
2
3
4
5
6
7
8
9
df_map_info[, "id"] = (1:nrow(df_map_info)) - 1
head(df_map_info)
## CTPRVN_CD CTP_ENG_NM CTP_KOR_NM id
## 0 42 Gangwon-do 강원도 0
## 1 41 Gyeonggi-do 경기도 1
## 2 48 Gyeongsangnam-do 경상남도 2
## 3 47 Gyeongsangbuk-do 경상북도 3
## 4 29 Gwangju 광주광역시 4
## 5 27 Daegu 대구광역시 5

rownames() 함수를 사용해도 되긴 한데 추가로 as.numeric() 함수를 사용해서 숫자로 변환해야 하기 때문에 위와 같이 처리를 해주었다. 해당 정보를 기반으로 원하는 시도만 골라내어 시각화 할 수 있다.

시군구

시군구 경계 시각화 절차는 시도 경계 시각화 절차와 같으며 코드와 그 시각화 결과는 다음과 같다.

1
2
3
4
5
6
7
map = readOGR("TL_SCCO_SIG.shp")
df_map = fortify(map)
ggplot(data = df_map,
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
theme(legend.position = "none")

행정경계지도 - 시군구

참고로 시군구 데이터 또한 시도 데이터 처리와 같이 id에 해당하는 행정구역을 확인할 수 있는데 다음과 같다.

1
2
3
4
5
6
7
8
9
df_map_info = map@data
head(df_map_info)
## SIG_CD SIG_ENG_NM SIG_KOR_NM
## 0 42110 Chuncheon-si 춘천시
## 1 42130 Wonju-si 원주시
## 2 42150 Gangneung-si 강릉시
## 3 42170 Donghae-si 동해시
## 4 42190 Taebaek-si 태백시
## 5 42210 Sokcho-si 속초시

시군구 단위 데이터이기 때문에 법정동 코드가 다섯 자리 까지 기록되어있다. 만약 특정 시도 기준으로 처리하고 싶다면, 다음과 같이 신규 변수를 생성하자.

1
2
3
4
5
6
7
8
9
10
11
df_map_info[, "id"] = (1:nrow(df_map_info)) - 1
df_map_info[, "SIDO"] = as.numeric(substr(df_map_info$SIG_CD,
start = 1, stop = 2))
head(df_map_info)
## SIG_CD SIG_ENG_NM SIG_KOR_NM id SIDO
## 0 42110 Chuncheon-si 춘천시 0 42
## 1 42130 Wonju-si 원주시 1 42
## 2 42150 Gangneung-si 강릉시 2 42
## 3 42170 Donghae-si 동해시 3 42
## 4 42190 Taebaek-si 태백시 4 42
## 5 42210 Sokcho-si 속초시 5 42

만약 강원도만 뽑고 싶다면 법정동 코드 첫 두 자리가 42로 시작하는 행정구역의 id를 추출하면 된다. 코드 및 결과는 다음과 같다.

1
2
3
4
5
6
id_sido = df_map_info[df_map_info$SIDO == 42, "id"]
ggplot(data = df_map[df_map$id %in% id_sido, ],
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
theme(legend.position = "none")

행정경계지도 - 시군구(강원도)

읍면동

읍면동 경계 시각화 절차는 시도 경계 시각화 절차와 같으며 코드와 그 시각화 결과는 다음과 같다.

1
2
3
4
5
6
7
map = readOGR("TL_SCCO_EMD.shp")
df_map = fortify(map)
ggplot(data = df_map,
aes(x = long, y = lat,
group = group, color = id)) +
geom_polygon(fill = "#FFFFFF") +
theme(legend.position = "none")

행정경계지도 - 읍면동

기타

참고로 여기서 사용하는 지리 좌표계는 범용적으로 사용하는 WGS84 좌표계가 아니다. 좌표계 변환과 관련해서는 R) 전처리 - 지리 좌표계(CRS) 변환 포스팅을 참고하도록 하자.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×