공간 데이터 시각화

bitSpatial 패키지를 이용한

유충현

한국R사용자회 / 디플래닉스

May 23, 2024

목차

  1. bitSpatial 배경
  2. bitSpatial 소개
  3. bitSpatial 특징
  4. bitSpatial 사용하기
  5. 질의 응답

bitSpatial 배경

지역 기반의 통계 분석 니즈

지역 기반의 공공 데이터

  • 지역 및 행정구역 기반으로 제공되는 다수의 공공 데이터
    • 지역에 대한 경제, 사회, 문화 등의 이해는 중요한 사회과학적인 요소

시각화를 통한 직관적인 정보 공유

  • 수치지도 기반으로 집계하여 지도상에 시각화
    • 지역에 대한 이해는 좀더 직관적인 시각화 방법이 유용

그러나 현실적인 한계

  • 데이터를 지역 기준으로 집계하기 어려움
  • 집계한 통계를 시각화하는 방법의 허들 존재
    • 협소한 지도 데이터의 리소스
  • 공공 기관마다 데이터 표준의 상이함

행정구역 체계의 일반화 이슈

  • 삽화소스: 국토지리정보원 청소년을 위한 국가지집(2022 기준)

표준화된 구조

  • 광역시도
    • 특별시 + 특별자치시 + 특별자치도 + 광역시 + 도
  • 시군구
    • 시 + 군 + 구
  • 읍면동
    • 읍 + 면 + 동

예외적인 구조

  • 세종특별자치시의 구조
    • 2단계 계층
    • 광역시도 > 시군구 레벨 없음 > 읍면동
  • 충청남도 천안시 동남구 신방동
    • 4단계 계층
    • 도 + 시 + 구 + 읍면동
    • 인구가 팽창하는 도에 포함된 일부 시의 경우 (구) 레벨을 포함하기도 함
      • 경기도 수원시, 경기도 성남시, 경기도 고양시 등

행정구역 체계의 변화 이슈

  • 삽화소스: 국토지리정보원 청소년을 위한 국가지집(2022 기준)

추이 분석의 이슈

  • 지역에 대한 통계가 동일시점 현황 분석을 목적으로 한다면 문제 없음
  • 시점별 통계의 변화를 살피는 추이분석을 수행할 경우에는 문제가 발생
    • 행정구역 체계의 변경으로,
    • 과거의 통계를 현재의 행정구역에 매핑할 수 없는 경우 발생

법정동이냐 행정동이냐 그것이 문제로다

  • 광역시도 > 시군구 > 읍면동 레벨에서의 읍면동은 두 가지 기준

법정동

  • 법으로 정한 동의 의미로, 법으로 정한 행정구역의 단위
  • 1914년 시행된 행정구역 통폐합때 정한 것으로, 현재까지 거의 변동이 없음

행정동

  • 행정능률, 주민편의에 의해 설정한 행정구역의 단위
  • 인구수 기반으로 나뉘어지며 주민센터에서 관리하는 구역
  • 편의에 따라 분할/병합 등의 변경 및 폐지가 발생함

법정동

  • 법정동은 변동이 적기 때문에 법정동의 집계기준으로 가져가면,
    • 추이분석의 장점이 있지만,
    • 공공 데이터에서 법정동 기준으로 배포하는 통계가 상대적으로 적음

행정동

  • 행정동은 변동이 많기 때문에 행정동의 집계기준으로 가져가면,
    • 추이분석의 단점이 있지만,
    • 공공 데이터에서 행정동 기준으로 배포하는 통계가 상대적으로 많음
    • 많은 파생 통계가 인구통계 기반으로 작성되기 때문에 인구통계는 중요한 통계

기타 이슈

읍면동 행정구역 레벨 집계의 어려움

  • 지번 주소에서 이제는 도로명 주소가 표준 주소 체계로 사용되고 있음
    • 도로명 주소는 길게 이어진 도로를 기준으로 만들어진 체계
    • 구역을 분할하는 기준이 아님
  • 집계해야할 지역기반의 Raw 데이터는 지번주소, 도로명주소가 혼용
    • 도로명 주소 정보로 행정동이나 법정동으로 집계

비표준화된 관리 체계

  • 행정안전부에서 관리하는 행정동 코드와 통계청에서 관리하는 행정동 코드 체계가 다름
  • 공공 데이터를 통합하여 데이터를 분석할 때에는 데이터 수요자가 이 문제를 해결

공공재로서의 수치지도의 한계

  • 공공 기관에서 배포하는 행정구역 경계 수치지도 부족 및 오류 포함
  • 행정구역 체계의 변화 미지원
    • 변화하는 행정 구역체계에 따라 히스토리컬하게 배포하지 않음

bitSpatial 소개

bitSpatial 소개

bitSpatial 정의

  • 통계지리정보(SGI; Statistical Geographic Information) 개발을 위한 R 패키지
    • 광역시도 > 시군구 > 읍면동 레벨의 행정구역 경계 수치지도에 공공데이터를 집계
    • 이를 시각화하는 일련의 리소스를 제공
  • Title: Tools for Spatial Analysis in Korean Regions
  • Author: Choonghyun Ryu
  • Maintainer: Choonghyun Ryu
  • GPL-2.0 license
  • URL: https://github.com/bit2r/bitSpatial

bitSpatial 설치

devtools::install_github("bit2r/bitSpatial")

제공하는 리소스

  • 광역시도 > 시군구 > 읍면동 레벨의 행정구역 경계 수치지도 제공
    • 수치지도를 sf 객체로 가공하여 제공 (매년 6월 기준으로 배포)
    • 17 광역시도
    • 250 시군구
    • 3,528 읍면동
  • 수치지도와 결합한 행정구역별로 집계된 통계
    • 40개 통계
    • 인구통계, 초중고 학교 통계, 병원/약국 통계
    • 집계 통계는 계속 추가할 예정
  • 수치지도와 조인할 수 있는 집계된 통계
    • 성별/연령대별 인구수
  • 수치지도와 조인할 수 있는 위치정보 데이터
    • 초중등학교 위치 데이터
    • 약국/병원 위치 데이터
    • 상가 위치 데이터

지리 기반 연산을 위한 기능

  • 위치 좌표 기반 연산
    • 두 좌표의 거리 구하기
    • 경위도 좌표계 위치정보의 좌표계 변환
  • 지리기반 집계를 위한 메타
    • 우편번호 행정동 매핑 데이터
    • 위도/경도로 행정구역 코드와 이름 가져오기

SGI 시각화를 위한 기능

  • 주제도 시각화
  • 최적 지도 이미지 사이즈 계산
  • map 시각화용 ggplot2 테마

리소스의 원천

  • 통계청의 통계지리서비스에서 배포하는 지역경계 수치지도
    • 행정동 기준의 지역경계 수치지도
      • 광역시도 > 시군구 > 읍면동 레벨의 지역경계 수치지도
    • 매년 주기적으로 배포하는 장점으로 선정
    • 매년 6월 기준으로 배포

제공하는 통계

bitSpatial 특징

sf 패키지 기반으로 저작

  • sf 패키지의 기능을 활용하여 데이터와 랜더링 처리
    • sf 패키지는 공간 벡터 데이터용 패키지로 sp 패키지 대체
      • sf 패키지는 GDAL, PROJ, GEOS 라이브러리를 사용
        • GDAL: reading and writing data
        • GEOS: geometrical operations
        • PROJ: projection conversions and datum transformations
    • 2023년 10월 16일 rgdal 프로젝트 종료
      • 역사의 뒤안길로 사라진 rgdal, 그리고 maptools, rgeos
    • bitSpatial는 sp 패키지 기반으로 시작해서 sf 패키지 기반으로 전환

리아스식 해안의 이슈 해결

  • 해안선 데이터의 복잡성으로 인한 메모리 사용과 계산 시간의 이슈
    • bitSpatial는 단순화를 통해 메모리 사용과 계산 시간을 개선
  • 지형지물을 나타내는 데 사용되는 정보의 양을 줄이는 방법
    • 지형지물(예: 작은 섬) 제거
    • 지형지물 결합하거나(예: 여러 개의 겹치거나 인접한 지형지물 결합)
    • 지형지물 복잡성 줄임(예: 호수와 같은 구멍 제거)
  • 정보 손실이 수반됨
    • 얼마나 단순화할지,
    • 이로 인해 결과에 어떤 편향성이 발생하는지 고려해야 함
  • 원본 Shapefile 랜더링 속도

  • 단순화된 Shapefile 랜더링 속도

  • 원본 Shapefile Polygon

  • 단순화된 Shapefile Polygon

bitSpatial 사용하기

주제도 그리기

  • thematic_map() 함수 원형
thematic_map(
  zoom = c("mega", "cty", "admi"),
  subset = NULL,
  stat = NULL,
  polygon = TRUE,
  point = FALSE,
  label = NULL,
  col_cnt = 9,
  palette = "YlOrRd",
  line_col = "darkgray",
  fill = "lightblue",
  point_col = "blue",
  title = NULL,
  subtitle = NULL,
  legend_pos = c("none", "right", "left", "bottom", "top"),
  base_family = "NanumSquare"
)
  • 광역시도 레벨
thematic_map(stat = "인구수", 
             title = "광역시도별 인구분포 현황",
             legend_pos = "right")

  • 시군구 레벨
thematic_map(zoom = "cty", stat = "인구수", 
             title = "시군구별 인구분포 현황",
             legend_pos = "right")

  • 제주특별자치도 읍면동 레벨
thematic_map(zoom = "admi", stat = "병원수", 
             subset = mega_nm %in% "제주특별자치도",
             title = "제주도 병원 현황")

  • 제주특별자치도 추자도 제거
thematic_map(zoom = "admi", stat = "병원수", 
             subset = mega_nm %in% "제주특별자치도" & !admi_nm %in% "추자면",
             title = "제주도 병원 현황")

  • 가구수
thematic_map(zoom = "admi", stat = "가구수", 
             subset = mega_nm %in% "제주특별자치도" & !admi_nm %in% "추자면",
             title = "제주도 가구수 현황")

  • 여성인구수
thematic_map(zoom = "admi", stat = "여성인구수", 
             subset = mega_nm %in% "제주특별자치도" & !admi_nm %in% "추자면",
             title = "제주도 여성인구수 현황")

  • point
thematic_map(zoom = "admi",subset = cty_nm %in% "노원구",
             stat = "household", line_col = "black", fill = "grey90",
             polygon = FALSE, point = TRUE, point_col = "Red")

  • point + polygon
thematic_map(zoom = "admi",subset = cty_nm %in% "노원구",
             stat = "household", 
             polygon = TRUE, point = TRUE, legend_pos = "right")

  • label = “name”
thematic_map(zoom = "admi", subset = mega_nm == "서울특별시" & cty_nm %in% "양천구", 
             stat = "age_mean", label = "name",
             title = "서울 양천구 인구통계 주제도",
             subtitle = "동별 평균 연령 현황", palette = "Purples", legend_pos = "right")

  • label = “all”
thematic_map(zoom = "admi", subset = mega_nm == "서울특별시" & cty_nm %in% "양천구", 
             stat = "age_mean", label = "all",
             title = "서울 양천구 인구통계 주제도",
             subtitle = "동별 평균 연령 현황", palette = "Purples", legend_pos = "right")

위치정보 시각화

  • 초중고등학교 위치정보
    • school
  • 약국 위치정보
    • pharmacy_info
  • 병원 위치정보
    • hospital_info
  • 상가 위치정보
    • 서울특별시: store_info_seoul
    • 경기: store_info_gyeonggi
    • 중부지역: store_info_middle
    • 남부지역: store_info_south
  • 서울시 입시·교과학원 분포 시각화 소스
pos_edu <- store_info_seoul |> 
  filter(industry_s_nm %in% "입시·교과학원") |> 
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

ggplot() +
  stat_density_2d(data = pos_edu, 
                  mapping = aes(x = purrr::map_dbl(geometry, ~.[1]),
                                y = purrr::map_dbl(geometry, ~.[2]),
                                fill = stat(density)),
                  geom = 'tile', contour = FALSE, alpha = 0.7) +
  scale_fill_viridis_c(option = "viridis", direction = -1) +
  geom_sf(data = cty |> filter(mega_nm %in% "서울특별시"),
          color = "grey30", fill = NA, linewidth = 0.8) +
  geom_sf(data = pos_edu, color = "blue", size = 0.1) +  
  xlim(126.75, 127.22) + ylim(37.42, 37.71) + 
  labs(title = "서울특별시 입시·교과학원 분포 현황",
       subtitle = "출처: 공공데이터포털의 소상공인시장진흥공단_상가(상권)정보") +
  theme_custom_map()

공간 데이터 연산

  • optimal_map_size() 함수원형
optimal_map_size(map, 
  width = 800, height = NULL)
  • 플롯팅할 지도의 종횡비를 고려하여, 최적의 이미지 크기 계산
optimal_map_size(mega)
$width
[1] 800

$height
[1] 853
optimal_map_size(
  mega, height = 600)
$width
[1] 800

$height
[1] 600
optimal_map_size(
  mega |> 
  filter(mega_nm %in% "서울특별시"))
$width
[1] 800

$height
[1] 662
  • calc_distance() 함수원형
calc_distance(
  lon1,
  lat1,
  lon2,
  lat2,
  proj = c("WGS84", "Bessel", "GRS80", "KATECH")
)
  • 두 좌표의 거리를 미터(m) 단위로 계산
calc_distance(132.12, 37.23, 133.45, 37.32)
[1] 118105
  • 함수원형
position2mega(x, y, 
  proj = c("WGS84", "Bessel", "GRS80", "KATECH"))
position2cty(x, y, 
  proj = c("WGS84", "Bessel", "GRS80", "KATECH"))
position2admi(x, y, 
  proj = c("WGS84", "Bessel", "GRS80", "KATECH"))
  • 경위도 좌표계 위치정보(경도, 위도)로 행정구역 정보를 구함
x <- c(126.9691, 127.4926) 
y <- c(37.56825, 36.23795)
# 광역시도 정보 구하기
position2mega(x, y)
       lon      lat mega_cd    mega_nm
1 126.9691 37.56825      11 서울특별시
2 127.4926 36.23795      44   충청남도
# 시군구 정보 구하기
position2cty(x, y)
       lon      lat mega_cd    mega_nm cty_cd cty_nm
1 126.9691 37.56825      11 서울특별시  11110 종로구
2 127.4926 36.23795      44   충청남도  44710 금산군
# 읍면동 정보 구하기
position2admi(x, y)
       lon      lat mega_cd    mega_nm cty_cd cty_nm    admi_cd admi_nm
1 126.9691 37.56825      11 서울특별시  11110 종로구 1111053000  사직동
2 127.4926 36.23795      44   충청남도  44710 금산군 4471039000  추부면

bitSpatial 응용

  • plotly와 bitSpatial을 활용한 대시보드 구현

  • bitReport와 bitSpatial을 활용한 정형 보고서 작성

질의 응답