R프로그래밍 - ggplot 한국지도 그리기

ggplot2 으로 지도를 그리는 건 생각보다 많은 지식을 필요로 하고, 데이터를 이해하지 못하면 지도를 그리기조차 쉽지 않다. 한국지도를 받는 곳은 국가공간정보포털이나 GIS DEVELOPER 에서 shp 파일을 받을 수 있다. Shapefile (SHP 파일)은 지리 정보 시스템 (GIS)에서 공간 데이터를 저장하는 데 사용되는 형식 중 하나이다. ESRI (Environmental Systems Research Institute)에서 개발되었으며, 공간 데이터를 기하학적 요소와 속성 데이터로 나누어 저장하는 형식이다.

shapefile

Shapefile은 여러 파일로 이루어져 있으며, 일반적으로 다음과 같은 세 가지 파일로 구성된다.

  • .shp (Shape 파일): 기하학적 요소를 저장하는 파일로, 점, 선, 면 등의 기하학적 형상을 정의한다.
  • .shx (Shape Index 파일): Shape 파일의 기하학적 요소에 대한 인덱스를 포함하고 있어, GIS 소프트웨어가 Shape 파일 내의 요소를 빠르게 찾을 수 있다.
  • .dbf (Attribute 데이터베이스 파일): 속성 데이터를 저장하는 파일로, 각 기하학적 요소에 대한 속성 정보를 테이블 형태로 가지고 있다.

GIS DEVELOPER 웹사이트에 가서, 23년 7월기준 시도 경계 데이터를 받는다. 지도데이터를 그대로 사용할 수 있으면 좋으련만, 몇가지 정비를 해야 한다. 참고로 다운받는 페이지에 보면 좌표계 정보가 표시되어 있는데, EPSG:5179 를 기억해놓자.

지도 데이터 정비

shp 파일을 read_sf 함수를 이용해서 읽는다.

library(sf)
sido1 = read_sf('~/Documents/map/sido_20230729/ctprvn.shp')

데이터의 구조를 파악하고자 print 를 해보는데, CRS 정보가 NA 로 되어 있는게 눈에 띈다. CRS는 Coordinate Reference System 의 약자로 좌표참조체계이다.

print(sido1)

Simple feature collection with 17 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 746110.3 ymin: 1458754 xmax: 1387950 ymax: 2068444
CRS:           NA    # NA로 셋팅되어 있다.

아까 위에서 기억하라고 했던 EPSG:5179 을 셋팅해야 한다. st_set_crs 함수를 이용해서 셋팅할 수 있고, 5179 숫자만 입력하면 CRS 정보가 셋팅된다. 지도데이터를 받을 때, 어떤 좌표계 기준의 데이터인지 꼭 확인하고 사용해야 한다.

sido2 = sido1 |> 
  st_set_crs(5179)

print(sido2)

Simple feature collection with 17 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 746110.3 ymin: 1458754 xmax: 1387950 ymax: 2068444
Projected CRS: Korea 2000 / Unified CS  # EPSG:5179 로 셋팅됨.

UFT-8 인코딩 기반에서 shp 파일을 불러올때, \xbc\xad\xbf\xef...으로 한글이 이상하게 출력된다면 인코딩 변환처리가 필요하다. 이런 현상은 맥(Mac) 환경에서 주로 볼 수 있다.

# A tibble: 17 × 4
   CTPRVN_CD CTP_ENG_NM        CTP_KOR_NM                                                                geometry
   <chr>     <chr>             <chr>                                                               <MULTIPOLYGON>
 1 11        Seoul             "\xbc\xad\xbf\xef\xc6\xaf\xba\xb0\xbd\xc3"               (((966987.2 1941111, 966 2 26        Busan             "\xba\xce\xbb\xea\xb1\xa4\xbf\xaa\xbd\xc3"               (((1148195 1685460, 1148 3 27        Daegu             "\xb4\xeb\xb1\xb8\xb1\xa4\xbf\xaa\xbd\xc3"               (((1087860 1760097, 1087

iconv 함수를 이용해서 EUC-KR 인코딩을 UTF-8 로 변경해서 정상출력 되도록 했다. 윈도우 환경에서 처음부터 한글이 제대로 출력된다면 이 작업은 하지 않아도 된다.

sido3 = sido2 |> 
  mutate(CTP_KOR_NM = iconv(CTP_KOR_NM, from = "EUC-KR", to = 'UTF-8'))

# A tibble: 17 × 4
   CTPRVN_CD CTP_ENG_NM        CTP_KOR_NM                                                                geometry
 * <chr>     <chr>             <chr>                                                           <MULTIPOLYGON [m]>
 1 11        Seoul             서울특별시     (((966987.2 1941111, 966987.1 1941111, 966961.3 1941130, 966930.6 2 26        Busan             부산광역시     (((1148195 1685460, 1148191 1685459, 1148181 1685456, 1147965 1685 3 27        Daegu             대구광역시     (((1087860 1760097, 1087860 1760098, 1087846 1760109, 1087839 1760

마지막으로 할 일이 있는데, 지도 데이터 사이즈를 줄여야 한다. 너무 정밀한 데이터다보니, ggplot 실행하는데 12초정도 걸리고, 그게 끝이 아니라 Rstudio plot 창에 뜨는데도 10초 이상걸리다보니, 결국 그래프를 한번 보려면 30초씩은 걸린다.

tic()
ggplot(sido3) +
  geom_sf()
toc() # 12.677 sec elapsed

너무 정밀한 데이터는 그래프를 랜더링하는데 시간이 오래걸릴 뿐만 아니라, 지도 경계선들이 매끈한 선의 느낌이 나질 않는다. 지도 데이터를 줄이는 함수들이 여럿있긴 하지만, 그 중 rmapshaper::ms_simplify 함수를 사용하였다. keep 파라메터에 줄이는 정도를 입력하면 되는데, 1/100 로 줄이려면 0.01 로 입력하면 된다.

library(rmapshaper)

sido4 = sido3 |> 
  ms_simplify(keep = 0.01, keep_shapes = T)

지도 그리기

드디어 지도를 그릴 준비가 완료되었다. 데이터만 준비되었다면 그래프를 그리는 건 쉽다. 1/100 으로 데이터를 줄였어도 충분히 정밀해보인다.

ggplot(sido4) +
  geom_sf()

전체 지도말고, 서울/경기도쪽을 확대하려면 coord_sf 함수를 사용해야 한다. 여기서 경도/위도를 기준으로 범위를 제한하고 싶다면, crs4326으로 셋팅해야 한다.

ggplot(sido4) +
  geom_sf() +
  coord_sf(crs = sf::st_crs(4326),
           xlim = c(126, 128), 
           ylim = c(37, 38))

시도별로 색상을 넣고, 폰트도 조금 바꿔서 만들 수 있다.


더 보면 좋을 글들