从shapefile和CSV生成传单映射的闪亮应用程序太慢;崩溃的大型shapefile

时间:2017-05-19 10:54:41

标签: r shiny leaflet

我几乎是R和Shiny的初学者。我一直在尝试构建一个可以让我探索原始目的地流量数据的应用。它接收一个具有原始区号,目标区号和一堆信息字段的csv,以及区域的shapefile作为多边形,并让我描绘流入或流出特定区域的流。我实际上有一个可行的Shiny应用程序,但它效率太低或者这可能不适合这项工作。我将粘贴下面的代码。

我的问题是它似乎过于耗费内存。对于少数区域,它可以工作,但速度极慢(最初加载需要很长时间,每次更改其中一个输入时更新)。更大的问题是我通常使用大型区域系统,有数百甚至数千个区域,总顶点数百万。当我尝试将其应用于其中一个大型区域系统时,它最初会加载,但是在两次或三次移动(缩放或平移)或只有一次输入更改后,RStudio会崩溃。

所以我想知道是否有任何方法可以提高效率,或者如果RStudio / Shiny根本不适合用于这样的事情。请记住,我对优化几乎一无所知,所以如果你对这个解释稍微贬低,我会很感激。

这是适用的应用程序:

library(shiny)
library(rgdal)
library(sp)
library(leaflet)
library(RColorBrewer)

shape <- readOGR(dsn=".", layer="zones")
shape <- spTransform(shape, CRS("+init=EPSG:4326"))

读取shapefile,然后读取数据。 &#39;我&#39;和&#39; j&#39;是原始和目的地区号码。我还需要要映射的可能字段列表,它必须由完全数字的字段组成。我还没弄明白如何在没有循环的情况下对其进行分组。任何有关这方面的帮助将不胜感激。

values <- read.csv("values.csv")
zonelist <- sort(unique(c(values$i, values$j)))
zones <- length(zonelist)
fieldlist <- as.vector(character())
fields <- 0
for (valname in colnames(values)){
  if (valname != "i" &
      valname != "j" &
      is.numeric(values[[valname]])
      ) {
        fields <- fields + 1
        fieldlist[fields] <- valname
        }
}

我把它们放在一个矩阵中。同样,欢迎任何更有效的方法。

mm <- array(numeric(), dim=c(fields,zones,zones))
mm[,,] <- 0
valarray <- array(numeric(), dim=fields*zones*zones)
valarray[] <- 0
for (k in 1:length(values)){
  if (colnames(values)[k] %in% fieldlist) {
    valarray[(values$j - 1) * zones * fields
             + (values$i - 1) * fields
             + which(colnames(values)[k] == fieldlist)
             ] <- values[[k]]
  } 
}
mm[,,] <- valarray

界面简单:选择/到参考区域,选择hte参考区域,选择要映射的数字字段。显示流量的传单地图。

ui <- fluidPage(
   titlePanel("Interzonal values mapping"),
   sidebarLayout(
      sidebarPanel(
         selectInput(inputId = "ft", label = "From/to zone", choices = c("from", "to")),
         selectInput(inputId = "zn", label = "Reference zone", choices = zonelist),
         selectInput(inputId = "fl", label = "Field to map", choices = fieldlist),
         width = 2
      ),
   mainPanel(leafletOutput("mapped_zonelist", height = 800),
             width = 10)
   )
)

服务器根据输入选择从矩阵mm作为矢量(vs)映射的流量,并根据流量值格式化区域映射。

server <- function(input, output) {
   m <- reactive({
     if (input$ft == "from"){
        vs <- mm[which(input$fl == fieldlist), as.numeric(input$zn), ]
       } else {
        vs <- mm[which(input$fl == fieldlist), , as.numeric(input$zn)]
       }

     c_weight <- numeric()
     c_weight[1:zones] <- 1
     c_weight[as.numeric(input$zn)] <- 2
     c_color <- character()
     c_color[1:zones] <- "blue"
     c_color[as.numeric(input$zn)] <- "black"

     leaflet() %>%
       addTiles() %>%
       addPolygons(data=shape,
                   fillOpacity = 0.7,
                   opacity = 0.8,
                   weight = c_weight[shape@data$n],
                   color = c_color[shape@data$n],
                   fillColor = colorNumeric(palette = brewer.pal(10, "RdBu"), domain = vs)(vs[shape@data$n]),
                   popup = paste0("Zone ", input$zn, " to zone ", as.character(shape@data$n),
                                  "<br>",
                                  input$fl, " = ", vs)
                  )
   })
   output$mapped_zonelist <- renderLeaflet(m())
}

shinyApp(ui = ui, server = server)

欢迎任何帮助。谢谢!

1 个答案:

答案 0 :(得分:0)

根据shapefile的大小/复杂程度,我要做的第一件事就是简化它。正常加载shapefile,然后:

install.packages("rmapshaper")
shape_simp <- rmapshaper::ms_simplify(shape, keep = 0.05)

试验keep =参数。数字越大,结果shapefile的复杂性就越大。这可能无法完全解决您的问题,但这是一个开始。