针对具有许多“层”的详细地图优化Shiny + Leaflet性能

问题描述 投票:12回答:1

我想创建一个Shiny应用程序,其中等值区的着色基于用户可以从中选择的许多可能的定量变量之一的数值。在简单的情况下,这是直截了当的,但是当我们有20多个变量时,我不确定最佳实践,具有非常详细的形状文件(~2300多边形)。

变量可能完全相互独立,例如“总人口”或“平均温度”可能相关或不相关,但其中一些变量将具有时间关系,例如3个或更多时间点的“总人口” 。

我使用的主要shapefile之一是ABS Statistical Area 2。下面我给出澳大利亚的人口密度(总人口/面积)和放大的悉尼视图,以更好地传达我感兴趣的细节水平。

澳大利亚Australia悉尼Sydney

我已经读到了R中的shapefile,并使用ms_simplify()包中的rmapshaper函数大大降低了复杂性/点数。

现在,就Shiny和传单而言,这就是我一直在做的事情:

  • server中定义server.R对象之前,我构建了一个包含所有所需“层”的主要地图对象。也就是说,有许多addPolygon()的传单调用来定义每个“层”(组)的颜色。 # Create main map primary_map <- leaflet() %>% addProviderTiles( providers$OpenStreetMap.BlackAndWhite, options = providerTileOptions(opacity = 0.60) ) %>% # Layer 0 (blank) addPolygons( data = aus_sa2_areas, group = "blank" ) %>% # Layer 1 addPolygons( data = aus_sa2_areas, fillColor = ~palette_layer_1(aus_sa2_areas$var_1), smoothFactor = 0.5, group = "layer_1" ) %>% ... # Layer N addPolygons( data = aus_sa2_areas, fillColor = ~palette_layer_n(aus_sa2_areas$var_n), smoothFactor = 0.5, group = "layer_n" ) %>% ...
  • 然后使用hideGroup()隐藏第一层的所有条形图,以便地图的初始渲染看起来不傻。 hideGroup("layer_1") %>% hideGroup("layer_2") %>% ... hideGroup("layer_n")
  • 在Shiny应用程序中,使用单选按钮(layer_selection),用户可以选择他们想要查看的“图层”。我使用observeEvent(input$layer_selection, {})来观看单选按钮选项的状态。要更新绘图,我使用leafletProxy()hideGroup()隐藏所有组,然后使用showGroup()取消隐藏所选图层。

我为缺乏可重复的例子而道歉。

问题

  1. 如何优化我的代码?我渴望使其更高效和/或更易于使用。我发现使用hideGroup()的/ showGroup()为每个图层选择比使用addPolygon()到空白地图要快得多,但这会导致应用程序花费大量的时间来加载。
  2. 我可以更改我为多边形着色的变量,而无需重新绘制或再次添加这些多边形吗?为了澄清,如果我有2个不同的变量要绘制,两者都使用相同的形状数据,我是否必须做2个不同的addPolygon()调用?
  3. 是否有更自动的方法根据所需的调色板(来自viridis包装?)对每个层的多边形进行合理着色。现在我发现为每个变量定义一个新的调色板,相当繁琐,例如: palette_layer_n <- colorNumeric( palette = "viridis", domain = aus_sa2_areas$aus_sa2_areas$var_n )

边问题

ABS网站上的this map如何运作?它可以非常详细,但响应速度极快。将网格块详细信息与SA2(2310多边形)进行比较,示例如下:

ABS web based map

r shiny gis choropleth r-leaflet
1个回答
2
投票

既然你还没有得到任何答案,我会根据一个简单的例子发布一些可以帮助你的东西。

如果你的可重复性,那当然会更容易;我想从环顾四周你已经看到有几个相关的问题/要求(关于重新着色多边形),而它似乎并没有真正的解决方案使其成为任何发布(传单)。

通过以下解决方法,您应该能够避免多个addPolygons并且可以覆盖任意数量的变量(现在我只是将单个变量硬编码到modFillCol调用中)。

library(leaflet)
library(maps)
library(viridis)

mapStates = map("state", fill = TRUE, plot = FALSE)

# regarding Question 3 - the way you set the domain it looks equivalent
# to just not setting it up front, i.e. domain = NULL
myPalette <- colorNumeric(
  palette = "viridis",
  domain = NULL
)

mp <- leaflet(data = mapStates) %>%
  addTiles() %>%
  addPolygons(fillColor = topo.colors(10, alpha = NULL), stroke = FALSE)

# utility function to change fill color
modFillCol <- function(x, var_x) {
  cls <- lapply(x$x$calls, function(cl) {
    if (cl$method == "addPolygons") {
      cl$args[[4]]$fillColor <- myPalette(var_x)
    }
    cl
  })
  x$x$calls <- cls
  x
}

# modify fill color depending on the variable, in this simple example
# I just use the number of characters of the state-names
mp %>%
  modFillCol(nchar(mapStates$names))
© www.soinside.com 2019 - 2024. All rights reserved.