들어가며…

     이번 포스트에서는 공개되어 있는 공공 데이터 중 건물 데이터를 기반으로 서울시 및 강남구의 발전 과정을 간접적으로 알아보고자 한다. 공개되어 있는 새주소도로 건물 shape 데이터와 행정구역 shape, 그리고 한강을 표현하기 위한 하천구역 shape를 활용하였다. 모든 자료가 온전하게 구성되어 있진 않지만 행정안전부에서 공개하는 새주소도로 건물 및 도로 shape은 폴리곤 경계가 제대로 잘 구성되어 있기 때문에 (예를 들어 폴리곤 정합성 등) 사용하기에 편리하다. 다만, 우리나라 및 서울의 엄청난 건물 수로 인해서 plot을 하다보면 일반적인 pc에서는 한 세월이 걸린다는 현실적인 답답함이 발생한다. 물론 간단하게 시각화만 할 것이라면 QGIS 등의 GIS 툴을 이용하는 것이 더 편리하고 답답함을 덜 수 있다. 공간 데이터들에 대한 처리가 잘 되기 때문인데 아래와 같은 지도를 구성하는 데에 큰 어려움이 없고, 화면에 나타나는 것도 본인 pc를 기준으로 2초 정도에 완료된다.

QGIS를 통한 서울시 건물 분포 및 노후도(빨강 --> 초록)

Figure 1: QGIS를 통한 서울시 건물 분포 및 노후도(빨강 –> 초록)

 

     그럼에도 불구하고 python을 사용하지 못하는 나로서는 QGIS 시각화의 자동화를 구현해 내기 어렵기 때문에 시각화 과정 역시도 R을 통해서 하는 방법이 전체적인 반복 작업을 위해 이롭다고 판단했다. 따라서 조금 무겁더라도 옳은 방법을 정립하고 나면 물리적인 리소스를 추가 투입하거나 자료의 분할을 통해서 충분히 한계를 해결할 수 있을 것으로 기대한다.(python을 아직 잘 모르기 때문에 핑계를 대고 있는 듯하다.) 전체 과정은 다음의 3단계로 이루어진다.

       1. 수치적인 확인을 위한 히스토그램 작성

       2. 현재의 상태를 보여주는 시각화

       3. 시대별 변화를 한 눈에 보기 위한 애니메이션 작성

1. 수치적인 확인을 위한 히스토그램 작성

     최종 목표는 시각화이지만, 그 전에 시대에 따라서 서울시 및 강남구 내 건물의 증가가 얼마나 일어났는지 미리 확인해보면 얼마나 시각화 결과가 드라마틱할 지를 예상할 수 있을 것이다. 여러 자료들을 보면 서울시의 성장은 6.25 전쟁 이후에 한국의 경제 성장과 속도를 거의 같이 하고, 서울시의 핵인 4대문 안부터 발전했다. 현재는 한국 부동산 및 부촌의 상징 중 하나인 강남구는 서울시 중에서도 상대적으로 늦게 투자가 이루어지고, 발전이 진행된 구이다. 강남 개발은 영화를 통해서도 소개된 적이 있는데, 1970년대부터 시작되어 전국적인 건설 붐이었던 90년대까지 이어졌다. 이러한 상황을 수치와 그래프를 통해 알아보면 다음과 같다.

     shape파일을 다루기 위한 패키지인 sf package 와 r data handling의 핵심인 tidyverse를 불러온 후, 공공데이터를 boundary, river, bld로 각각 로드한다. 새주소도로 건물 데이터 내에 여러 속성 중 이번에 활용한 속성은 시군구 코드, 건축물 사용승인일자(건축년도로 해석함)로 모든 년도에 대해서 표현하면 너무 구체적인 히스토그램이 발생하여 그 전에 그룹을 10년 단위로 구성하여 부여하였다.(1950년도 이전의 건물들은 하나의 그룹으로 부여하였고, 데이터 입력 오류 등으로 인해 1900보다 작거나 2019보다 큰 건축년도를 갖는 건물은 제외하였다.)

# load packages
library(sf)
library(tidyverse)

# Load shape files
boundary <- st_read("data/CITY.shp")
river <- st_read("data/RIVER.shp")
bld <- st_read("data/BUILDING.shp")

# lighten and manipulate building
bld <- bld %>% select(sigungu = A3, year = A13) 
bld <- bld %>% 
    mutate(sigungu = substr(bld$sigungu, 1, 4), # 시군구코드 생성
           year = substr(bld$year, 1, 4)) %>%  # 사용승인일자의 년도만 추출
    filter(year >= 1900, year <= 2019) %>% # 일자가 유효하게 부여된 건물만 선택
    mutate(gr_year = case_when(year <= 1950 ~ "~ 1950", # 기간 부여
                               year > 1950 & year <= 1960 ~ "1951 ~ 1960",
                               year > 1960 & year <= 1970 ~ "1961 ~ 1970",
                               year > 1970 & year <= 1980 ~ "1971 ~ 1980",
                               year > 1980 & year <= 1990 ~ "1981 ~ 1990",
                               year > 1990 & year <= 2000 ~ "1991 ~ 2000",
                               year > 2000 & year <= 2010 ~ "2001 ~ 2010",
                               year > 2010 & year <= 2019 ~ "2010 ~ 2019"))

# building count by grouped year
bld_by_yr <- as.data.frame(bld) %>% 
    group_by(year = gr_year) %>% 
    summarize(cnt = n()) %>% 
    ungroup() %>% 
    mutate(cum = cumsum(cnt))
Table 1: The number of Buildings in Seoul & Gangnam
year Seoul count Seoul cumulative count Gangnam count Gangnam cumulative count
~ 1950 5688 5688 0 0
1951 ~ 1960 10636 16324 0 0
1961 ~ 1970 44659 60983 6 6
1971 ~ 1980 85481 146464 1565 1571
1981 ~ 1990 149189 295653 6392 7963
1991 ~ 2000 185604 481257 7628 15591
2001 ~ 2010 93026 574283 6064 21655
2010 ~ 2019 41458 615741 510 22165

 
     표1을 보면 서울은 1960년대부터 건물 증가가 급격히 들어나고, 1990년대에 정점을 이루었다. 서울의 물리적 영역의 포화 및 경제적 상황의 변화로 2000년대 이후로는 신규 건물의 건설은 크게 줄어들어 1960년대 수준으로 낮아졌다. 강남구 역시 시대에 따른 변화는 급격히 증가했다가 감소하는 패턴을 보이지만, 그 시기가 1970년대로 서울시에 비해서는 늦었다는 것을 숫자로 확인할 수 있다. 표로 제시된 결과를 그래프로 시각화하면 다음과 같다.

# building count by grouped year
plot_bld_by_yr <- ggplot(data = bld_by_yr, aes(x = year, y = cum)) +
    geom_bar(aes(y = cum), stat = "identity", fill = "gray50",
             color = "gray30") +
    geom_bar(aes(y = cnt), stat = "identity",
             fill = "gray", color = "gray 30") +
    geom_text(aes(label = scales::comma(cum)), vjust = -0.5) +
    labs(x = "Year", y = "count",
         title = "Seoul's Growth by the number of Buildings",
         subtitle = "A combined plot of building count and the cumulative summation thereof",
         caption = "source: nsdi.go.kr") +
    scale_y_continuous(label = scales::comma) +
    theme(plot.title = element_text(face = "bold", size = 14),
          plot.subtitle = element_text(face = "italic", size = 11),
          plot.caption = element_text(fac = "italic", size = 9),
          axis.ticks = element_line(size = 0))
서울시 히스토그램, 어두운 막대가 누적, 밝은 막대가 해당 기간

Figure 2: 서울시 히스토그램, 어두운 막대가 누적, 밝은 막대가 해당 기간

     같은 기준으로 강남구에 대해 그려보면 위의 설명을 더욱 직관적으로 확인할 수 있는데, data 수정 내용에서 ~ 1950과 1950 ~ 1960 기간에 대한 자료가 없기 때문에 0을 따로 추가하였다.

bld_by_yr_gn <- as.data.frame(bld) %>% 
    filter(sigungu == '1168') %>% 
    group_by(year = gr_year) %>% 
    summarize(cnt = n()) %>% 
    ungroup() %>% 
    rbind(list(c("~ 1950", "1951 ~ 1960"), c(0,0))) %>% 
    arrange(year) %>% 
    mutate(cum = cumsum(cnt))
plot_bld_by_yr_gn <- ggplot(data = bld_by_yr_gn, aes(x = year, y = cum)) +
    geom_bar(aes(y = cum), stat = "identity", fill = "gray50",
             color = "gray30") +
    geom_bar(aes(y = cnt), stat = "identity",
             fill = "gray", color = "gray 30") +
    geom_text(aes(label = scales::comma(cum)), vjust = -0.5) +
    labs(x = "Year", y = "count",
         title = "Gangnam's Growth by the number of Buildings",
         subtitle = "A combined plot of building count and the cumulative summation thereof",
         caption = "source: nsdi.go.kr") +
    scale_y_continuous(label = scales::comma) +
    theme(plot.title = element_text(face = "bold", size = 14),
          plot.subtitle = element_text(face = "italic", size = 11),
          plot.caption = element_text(fac = "italic", size = 9),
          axis.ticks = element_line(size = 0))
강남구 히스토그램, 어두운 막대가 누적, 밝은 막대가 해당 기간

Figure 3: 강남구 히스토그램, 어두운 막대가 누적, 밝은 막대가 해당 기간

2. 현재의 상태를 보여주는 시각화

     sf 패키지 내의 geom_sf함수를 통해서 shape 파일들에 대한 내용을 정의하고, ggplot의 제어함수들을 바탕으로 원하는 형태의 시각화 결과를 생성할 수 있다. 물론 geo로 시작하는 여러 패키지 들이나 GIS와 관련된 패키지를 이용하면 배경 지도를 깔아두고 공간 데이터를 시각화할 수 있으나 이 포스트에서 표현하고자하는 것은 지도 보다는 시각화 자체이므로 ggplot으로 구성하였다. sf 패키지는 ggplot의 문법을 통해 객체를 처리할 수 있기 때문에 기존 사용자들에게 큰 지지를 얻고 있다. 개인적으로도 rgeos, sp 패키지보다 sf가 속도면에서도 빠르고 기존 작업들과 연동할 수 있다는 점에서 큰 이점이 있다고 생각한다.

ggplot() +
    geom_sf(data = boundary, colour = "black", fill = "black") + # 서울경계 검정
    geom_sf(data = river, colour = "#97d7e0", fill = "#97d7e0") + # 한강 물색
    geom_sf(data = bld, aes(fill = gr_year, col = gr_year)) + # 건물 색상: 연도
    scale_fill_manual(name = "Construction year",
                      values = c("#d7191c", "#d7191c", "#d7191c", "#e6f5a8",
                                 "#e6f5a8", "#e6f5a8", "#6abd58", "#1a9641")) +
    scale_color_manual(name = "Construction year",
                       values = c("#d7191c", "#d7191c", "#d7191c", "#e6f5a8",
                                  "#e6f5a8", "#e6f5a8", "#6abd58", "#1a9641")) +
    scale_x_continuous(labels = NULL) + # 축 지우기
    scale_y_continuous(labels = NULL) + # 축 지우기
    labs(title = "Seoul's Buildings by Construction year") + # 제목 쓰고
    theme(axis.ticks = element_blank(), # 축 막대기 지우기
          axis.line = element_blank(), # 축 선 지우기
          axis.text = element_blank(), # 축 이름 지우기
          panel.background = element_blank(), # 배경 제거
          plot.title = element_text(face = "bold", size = 14) # 제목 서식 부여)
서울시 건물 분포 및 노후도(빨강 --> 초록)

Figure 4: 서울시 건물 분포 및 노후도(빨강 –> 초록)

3. 시대별 변화를 한 눈에 보기 위한 애니메이션 작성

     위와 같은 정적인 지도를 작성할 때에는 충분히 시각적으로 강조하고 싶은 부분이 옳게 강조되어야 하는데, 너무 많은 건물들을 하나의 그림 안에 그리다보니 시간에 따른 색상이 시각적으로 확인되지 않았다. 따라서 이러한 plot 자체를 시간의 순서에 따라서 나타나게 할 수 있는 애니메이션을 활용하여 표현해보았다. sf 패키지와 마찬가지로, ggplot의 뛰어난 시각화를 애니메이션으로 쉽게 변환할 수 있도록 gganimate 패키지가 있다. 이 패키지를 이용하여 plot 전환에 대한 함수 및 변수를 지정해주고 gif 등의 파일로 저장하면 손쉽게 애니메이션을 구성할 수 있다. 다만, ggplot을 일일이 그린 후에 애니메이션화하기 때문에 시간이 그만큼 오래 걸릴 수 있으며, 최근에 패키지의 저자가 문법을 크게 수정해서 인터넷 검색 시 현재 버전과 맞지 않는 예제가 많다는 단점이 있다.
     ggplot 객체들과의 연동을 통해서 아래와 같이 한 줄만 추가하면, 손쉽게 애니메이션을 생성하고 gif로 저장할 수 있다.

library(gganimate)
library(gifski)
library(png)
bld_gn <- bld %>% filter(sigungu == '1168') # 그리기 무거우니 강남구만
gn_gif <- ggplot() +
    geom_sf(data = boundary %>% filter(CTY_CD == '1168'), 
            colour = "black", fill = "black") +
    geom_sf(data = bld_gn) +
    scale_fill_manual(name = "Construction year",
                      values = c("#d7191c", "#ed6e43", "#feba6f", "#ffe8a5",
                                 "#e6f5a8", "#b3df76", "#6abd58", "#1a9641")) +
    scale_color_manual(name = "Construction year",
                       values = c("#d7191c", "#ed6e43", "#feba6f", "#ffe8a5",
                                  "#e6f5a8", "#b3df76", "#6abd58", "#1a9641")) +
    scale_x_continuous(labels = NULL) + 
    scale_y_continuous(labels = NULL) +
    labs(title = "Gangnam-gu Buildings in: {current_frame}") +
    theme(axis.ticks = element_blank(),
          axis.line = element_blank(),
          axis.text = element_blank(),
          panel.background = element_blank(),
          plot.title = element_text(face = "bold", size = 14)) +
    transition_manual(frames = gr_year, cumulative = TRUE) # 애니메이션화!

     만들고 보니 강남구도 잘 보이지 않는 것 같아서 단일 색으로 표현하여 공간적 확장을 조금 더 강조해보았다. 표지판처럼 검정 바탕에 노랑으로 부여하여…

gn_gif <- ggplot() +
    geom_sf(data = boundary %>% filter(CTY_CD == '1168'), 
            colour = "black", fill = "black") +
    geom_sf(data = bld_gn, aes(col = c("#FFD700"), fill = c("#FFD700"))) +
    scale_x_continuous(labels = NULL) + 
    scale_y_continuous(labels = NULL) +
    labs(title = "Gangnam-gu Buildings in: {current_frame}") +
    theme(axis.ticks = element_blank(),
          axis.line = element_blank(),
          axis.text = element_blank(),
          panel.background = element_blank(),
          plot.title = element_text(face = "bold", size = 14)) +
    transition_manual(frames = gr_year, cumulative = TRUE) # 애니메이션화!

     이상으로 시각화를 완료하였다. 나라에서 활용하라고 업로드해둔 데이터들을 통해서 할 수 있는 내용이 훨씬 더 많을테지만 간단하게 시각화만으로도 서울시 내의 건물 확장을 확인할 수 있었다. 사용한 새주소건물 데이터가 궁금하신 분은 아래의 링크로 가시면 된다.
         새주소 도로 건물 데이터