> ## 10-1 반응형 지도 만들기
>
> # Step 1 : 데이터 불러오기
>
> library(sf)
> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> load("./06_geodataframe/06_apt_price.rdata") # 아파트 실거래 데이터
> bnd <- st_read("./01_code/sigun_bnd/seoul.shp") # 서울시 경계선
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_bnd\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS: GCS_unknown
> load("./07_map/07_kde_high.rdata") # 최고가 래스터 이미지
> load("./07_map/07_kde_hot.rdata") # 급등 지역 래스터 이미지
>
> # Step 2 : 마커 클러스터링 설정
>
> load("./01_code/circle_marker/circle_marker.rdata") # 마커 클러스터링 함수(javascript avg.fomula)
> circle.colors <- sample(x=c("red","green","blue"), size = 1000, replace = TRUE)
>
> # Step 3 : 반응형 지도 만들기
>
> library(purrr)
> library(leaflet)
> library(raster)
> leaflet() %>%
+ #---# 기본 앱 설정 : 오픈스트리트맵
+ addTiles(options = providerTileOptions(minZoom = 9, maxzoom = 18)) %>%
+ #---# 서울시 외곽 경계선
+ addPolygons(data = bnd, weight = 3, color = "red", fillOpacity = 0) %>%
+ #---# 최고 지역 KDE
+ addRasterImage(raster_high,
+ colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_high)
+ , na.color = "transparent"), opacity = 0.4, group = "2021 최고가") %>%
+ #---# 급등지역 KDE
+ addRasterImage(raster_hot,
+ colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_hot)
+ , na.color = "transparent"), opacity = 0.4, group = "2021 급등지") %>%
+ #---# 레이어 스위치 메뉴
+ addLayersControl(baseGroups = c("2021 최고가", "2121 급등지"),
+ options = layersControlOptions(collapsed = FALSE)) %>%
+ #---# 마커 클러스터링
+ addCircleMarkers(data = apt_price, lng = unlist(map(apt_price$geometry,1)),
+ lat = unlist(map(apt_price$geometry,2)), radius = 10, stroke = FALSE,
+ fillOpacity = 0.6, fillColor = circle.colors, weight = apt_price$py,
+ clusterOptions = markerClusterOptions(iconCreateFunction=JS(avg.formula)))
Warning messages:
1: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
Discarded ellps WGS 84 in Proj4 definition: +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs
2: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
Discarded datum World Geodetic System 1984 in Proj4 definition
3: In wkt(projfrom) : CRS object has no comment
4: In wkt(pfrom) : CRS object has no comment
5: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
6: In wkt(pfrom) : CRS object has no comment
7: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
8: In rgdal::rawTransform(projto_int, projfrom, nrow(xy), xy[, 1], :
Using PROJ not WKT2 strings
9: In wkt(pfrom) : CRS object has no comment
10: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
>
>
> ## 10-2 지도 애플리케이션 만들기
>
> # Step 1 : 그리드 필터링하기
>
> grid <- st_read("./01_code/sigun_grid/seoul.shp") # 서울시 1km 그리드
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_grid\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 694 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS: GCS_unknown
> grid <- as(grid, "Spatial"); grid <- as(grid, "sfc")
> grid <- grid[which(sapply(st_contains(st_sf(grid), apt_price), length) > 0)] # 필터링
> plot(grid)
>
> # Step 2 : 반응형 지도 모듈화하기
>
> m <- leaflet() %>%
+ #---# 기본 앱 설정 : 오픈스트리트맵
+ addTiles(options = providerTileOptions(minZoom = 9, maxzoom = 18)) %>%
+ #---# 서울시 외곽 경계선
+ addPolygons(data = bnd, weight = 3, stroke = T, color = "red", fillOpacity = 0) %>%
+ #---# 최고 지역 KDE
+ addRasterImage(raster_high,
+ colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_high)
+ , na.color = "transparent"), opacity = 0.4, group = "2021 최고가") %>%
+ #---# 급등지역 KDE
+ addRasterImage(raster_hot,
+ colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_hot)
+ , na.color = "transparent"), opacity = 0.4, group = "2021 급등지") %>%
+ #---# 레이어 스위치 메뉴
+ addLayersControl(baseGroups = c("2021 최고가", "2121 급등지"),
+ options = layersControlOptions(collapsed = FALSE)) %>%
+ #---# 마커 클러스터링
+ addCircleMarkers(data = apt_price, lng = unlist(map(apt_price$geometry,1)),
+ lat = unlist(map(apt_price$geometry,2)), radius = 10, stroke = FALSE,
+ fillOpacity = 0.6, fillColor = circle.colors, weight = apt_price$py,
+ clusterOptions = markerClusterOptions(iconCreateFunction=JS(avg.formula))) %>%
+ #---# 그리드
+ leafem::addFeatures(st_sf(grid), layerId = ~seq_len(length(grid)), color = "grey")
Warning messages:
1: In wkt(projfrom) : CRS object has no comment
2: In wkt(pfrom) : CRS object has no comment
3: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
4: In wkt(pfrom) : CRS object has no comment
5: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
6: In rgdal::rawTransform(projto_int, projfrom, nrow(xy), xy[, 1], :
Using PROJ not WKT2 strings
7: In wkt(pfrom) : CRS object has no comment
8: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[, :
Using PROJ not WKT2 strings
> # ---> 이렇게 만든 m은 자바스크립트를 포함하는 htmlwidgets라고 하며 샤이니에서 불러와서 사용할 수 있다.
> m
>
> # Step 3,4 : 애플리케이션 구현하기
>
> library(shiny)
> install.packages("mapedit")
> library(mapedit)
> library(dplyr)
>
> ui <- fluidPage(
+ selectModUI("selectmap"), # 지도 입력 모듈. 지도에서 특정 지점이 선택될 때 입력값을 서버로 전달
+ textOutput("sel")
+ )
> server <- function(input, output, session) {
+ df <- callModule(selectMod, "selectmap", m) # 입력 결과를 처리하여 다시 화면으로 전달하는 출력 모듈
+ output$sel <- renderPrint({
+ if (nrow(df() > 0)) {
+ df()[1]
+ }
+ })
+ }
> shinyApp(ui, server)
Listening on http://127.0.0.1:5504
>
>
> ## 10-3 반응형 지도 애플리케이션 완성하기
>
> # Step 1 : 사용자 인터페이스 설정하기
>
> library(DT)
> ui <- fluidPage(
+ fluidRow(
+ column(9, selectModUI("selectmap"), div(style = "hegith:45px")),
+ column(3,
+ sliderInput("range_area", "전용면적", sep = "", min = 0, max = 350, value = c(0,200)),
+ sliderInput("range_time", "건축연도", sep = "", min = 1960, max = 2020, value = c(1980,2020))
+ ),
+ column(12, dataTableOutput("table"), div(style = "height:200px"))
+ )
+ )
>
> # Step 2 : 반응식 설정하기
>
> server <- function(input, output, session) {
+ apt_sel <- reactive({
+ apt_sel <- subset(apt_price,
+ con_year >= input$range_time[1] & con_year <= input$range_time[2] &
+ area >= input$range_area[1] & area <= input$range_area[2])
+ apt_sel
+ })
+
+ # Step 3 : 지도 입출력 모듈 설정하기
+
+ g_sel <- callModule(selectMod, "selectmap", m)
+
+ # Step 4 : 선택에 따른 반응 결과 저장하기
+
+ #---# 반응 초기값 설정(NULL)
+ rv <- reactiveValues(intersect=NULL, selectgrid=NULL)
+ #---# 반응 결과(rv:reactive value) 저장
+ observe({
+ gs <- g_sel()
+ rv$selectgrid <- st_sf(grid[as.numeric(gs[which(gs$selected == TRUE), "id"])])
+ if(length(rv$selectgrid) > 0) {
+ # 특정 그리드를 선택하면 해당 그리드 내 정보를 추출한 다음 rv$sel에 저장한다.
+ # st_drop_geometry()로 불필요한 공간 정보는 제거한다.
+ rv$intersect <- st_intersects(rv$selectgrid, apt_sel())
+ rv$sel <- st_drop_geometry(apt_sel()[apt_sel()[unlist(rv$intersect[1:10]),],])
+ } else {
+ rv$intersect <- NULL
+ }
+ })
+
+ # Step 5 : 반응 결과 렌더링
+
+ output$table <- DT::renderDataTable({
+ dplyr::select(rv$sel, ymd, addr_1, apt_nm, price, area, floor, py) %>%
+ arrange(desc(py))
+ }, extension = 'Buttons',
+ options = list(dom = 'Bfrtip', scrollY = 300, scrollCollapse = T, paging = TRUE, buttons = c('excel'))
+ )
+ }
>
> # Step 6 : 애플리케이션 실행하기
>
> shinyApp(ui, server)
Listening on http://127.0.0.1:5504
출처 : 김철민, ⌜공공데이터로 배우는 R 데이터분석 with 샤이니⌟, 이지스퍼블리싱, 2022
'데이터분석 > R' 카테고리의 다른 글
[R 데이터분석 with 샤이니] 아파트가격 상관관계 분석 (Shiny) (0) | 2022.07.26 |
---|---|
[R 데이터분석 with 샤이니] 데이터 분석 어플리케이션 개발하기 2 - app.R (0) | 2022.07.23 |
[R 데이터분석 with 샤이니] 샤이니 입문하기 (0) | 2022.07.19 |
[R 데이터분석 with 샤이니] 통계 분석과 시각화 (0) | 2022.07.12 |
[R 데이터분석 with 샤이니] 분석 주제를 지도로 시각화하기 (0) | 2022.07.05 |