从 geojson 数据创建动态数量的传单图层组

Create dynamic number of leaflet layergroups from geojson data

简介
我在 R 中非常敏捷,但我的 java 技能是不存在的。因此,我愿意任由你摆布来回答这个问题,希望不要太复杂,问题(否则我将很难找出答案 ;-))。

运行 下面的代码要求您从 github 下载三个传单插件(代码注释中的链接)。它们应该放在文件夹 ./script 中,相对于您 运行 代码所在的位置。

样本数据
我有 excel-张多条路线。为了简单起见,我已经使用以下代码读入了一个文件,所以我不必在线共享 excel-文件:

# read excel file
bestand <- "./data/CBM_Schuttorf_Buren.xlsx"
bladen <- readxl::excel_sheets(bestand)
xldata <- lapply(bladen, function(x) {
  readxl::read_excel(path = bestand, sheet = x,
                     col_types = c(rep(c("numeric", "text"), 2), rep("numeric", 2)))
})
names(xldata) <- bladen

这将产生以下对象,您需要使用该对象继续代码

bladen <- c("A1L", "A1R")

xldata <- list(A1L = structure(list(route = c(1, 1, 2, 2, 2, 3, 3, 3), 
    routeType = c("stremming", "stremming", "omleiding", "omleiding", 
    "omleiding", "omleiding", "omleiding", "omleiding"), punt = c(1, 
    2, 1, 2, 3, 1, 2, 3), puntType = c("start", "eind", "start", 
    "via", "eind", "start", "via", "eind"), lat = c(52.341823, 
    52.284989, 52.340234, 52.193045, 52.302415, 52.349596, 52.193045, 
    52.302415), lon = c(7.254037, 6.74575, 7.271095, 7.102321, 
    6.715246, 7.258845, 7.102321, 6.715246)), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -8L)), A1R = structure(list(
    route = c(1, 1, 2, 2, 2, 3, 3, 3), routeType = c("stremming", 
    "stremming", "omleiding", "omleiding", "omleiding", "omleiding", 
    "omleiding", "omleiding"), punt = c(1, 2, 1, 2, 3, 1, 2, 
    3), puntType = c("start", "eind", "start", "via", "eind", 
    "start", "via", "eind"), lat = c(52.284267, 52.341886, 52.303024, 
    52.19279, 52.354846, 52.303024, 52.19279, 52.339145), lon = c(6.754951, 
    7.251379, 6.713831, 7.104181, 7.258402, 6.713831, 7.104181, 
    7.285606)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-8L)))

我的问题
示例数据是一个简化的问题。只有两个列表条目,A1LA1R。在我的生产数据中会有更多条目。
结果,我想要的是下面代码的动态版本。在这里,我在各处对图层组 A1L 和 A1R 进行了硬编码。但是虽然这可以作为概念验证,但在生产中是行不通的。

如前所述,我需要几个传单插件的功能,所以我非常依赖 htmlwidgets::onRender()- 功能来获取我需要的东西。这也是我的致命弱点,因为我是 javascript 中的一个完整的 n00b。

期望输出
我愿意接受所有可以复制下面代码结果的建议,而无需对 filtering/Layergroups..

进行硬编码

注意:折线末端的箭头仅在传单显示在浏览器中时显示。他们 not 在 rstudio 查看器中显示(我很沮丧地发现了一个;-))

click here for my output

我的代码

library(tidyverse)
library(readxl)
library(osrm)
library(leaflet)
library(geojsonsf)

# below commented out, xldata is already provided

# # read excel file
# bestand <- "./data/myfile.xlsx"
# bladen <- readxl::excel_sheets(bestand)
# xldata <- lapply(bladen, function(x) {
#   readxl::read_excel(path = bestand, sheet = x,
#                      col_types = c(rep(c("numeric", "text"), 2), rep("numeric", 2)))
# })
# names(xldata) <- bladen

# split individual routes (will become polylines later on)
routes <- lapply(xldata, function(x) split(x, f = x$route))

# create real routes, using  osm routing
df <- 
  dplyr::bind_rows(
    lapply(seq.int(routes), function(i) {
    dplyr::bind_rows(
      lapply(seq.int(lengths(routes)[i]), function(j) {
          temp <- osrmRoute(loc = as.data.frame(routes[[i]][[j]][, c("lon", "lat")]),
                        overview = "full", returnclass = "sf") %>%
            mutate(naam = paste0(bladen[i], "_", routes[[i]][[j]][1,2], routes[[i]][[j]][1,1])) %>%
            mutate(groep = bladen[i]) %>%
            mutate(groepVol = paste0("groups.",bladen[i])) %>%
            mutate(type = ifelse(grepl("stremming", naam), "stremming", "omleiding"))
      }))
    })
  )
df

# get boundaries for map
grens <- sf::st_bbox(df) %>% as.vector()
# create named list of geojson routes
plotdata <- lapply(split(df, f = df$naam), sf_geojson)
#  PLUGIN SECTION
# from: https://github.com/slutske22/leaflet-arrowheads
arrowHead <- htmlDependency(
  "leaflet-arrowheads",
  "0.1.2",
  src = normalizePath(".\script"),
  #src = "./script",
  script = "leaflet-arrowheads.js"
)
# from https://github.com/makinacorpus/Leaflet.GeometryUtil
geometryutil <- htmlDependency(
  "leaflet.geometryutil",
  "0.1.2",
  src = normalizePath(".\script"),
  #src = "./script",
  script = "leaflet.geometryutil.js"
)
registerPlugin <- function(map, plugin) {
  map$dependencies <- c(map$dependencies, list(plugin))
  map
}
# plot the map and layers
leaflet() %>%
  #register plugins
  registerPlugin(arrowHead) %>%
  registerPlugin(geometryutil) %>%
  # add basemap
  addProviderTiles(providers$CartoDB.Positron) %>%
  # set map boundaries
  fitBounds( grens[1], grens[2], grens[3], grens[4]) %>%
  onRender("function(el, x, data) {
    // funciton to define line color based on 
    //  feature.properties.type
    function getColor(d) {
      return d == 'stremming' ? 'red' :
             d == 'omleiding' ? 'seagreen' :
                                'black';
    }
    // funciton to define line dash based on 
    //  feature.properties.type
    function getDash(d) {
      return d == 'stremming' ? '20' :
             d == 'omleiding' ? '' :
                                '';
    }
    // function to set style of polylines
    function newstyle(feature) {
      return {
          color: getColor(feature.properties.type),
          weight: 10,
          opacity: 1,
          dashArray: getDash(feature.properties.type),
          fillOpacity: 0.7
      };
    }
    ///////////////////////////////////////
    //would like to make the code below this dynamic 
    //based on the groep-property in the JSON object
    //so A1L and A1R groups (and thereby the filtering)
    //are read in directly from the data object df 
    ///////////////////////////////////////
    // filtering
    function A1L(feature) {if (feature.properties.groep === 'A1L') return true} 
    function A1R(feature) {if (feature.properties.groep === 'A1R') return true}
    // crteation of layergroups
    var groups = {
            A1L: new L.LayerGroup(),
            A1R: new L.LayerGroup()
    };
    // create layers and add to groups
    var A1L = L.geoJSON(data, { 
      filter: A1L,
      style: newstyle,
      arrowheads: {frequency: 'endonly', yawn: 45, size: '30px', fill: true}
    })
    .on('mouseover', function (e) {e.target.setStyle({weight: 15, opacity: 1 });})
    .on('mouseout', function (e) {e.target.setStyle({weight: 10, opacity: 0.75});})
    .addTo(groups.A1L);
    var A1R = L.geoJSON(data, { 
      filter: A1R,
      style: newstyle,
      arrowheads: {frequency: 'endonly', yawn: 45, size: '30px', fill: true}
    })
    .on('mouseover', function (e) {e.target.setStyle({weight: 15, opacity: 1 });})
    .on('mouseout', function (e) {e.target.setStyle({weight: 10, opacity: 0.75});})
    .addTo(groups.A1R);

     var baseLayers = {
      'A1L': A1L,
      'A1R': A1R
    };
    
    var layerControl = L.control.layers(baseLayers, null, {collapsed: false}).addTo(this);
    baseLayers['A1L'].addTo(this);
    
    }", data = sf_geojson(df)) 

到目前为止我尝试了什么

我找到了可能是解决方案的东西 ,但我缺乏 java 技能来:

  1. 看看这是否确实是要走的路,如果是的话:
  2. 如何在我的代码中实现这一点。

经过 >1 天的无果而终,我终于找到了 an answer 我可以一起工作。

它现在可以正常工作了,这是我对任何寻找这种可能性的人的回答(我在过去 24 小时内发现了一些 ;-))

# plot the map and layers
leaflet() %>%
  #register plugins
  registerPlugin(arrowHead) %>%
  registerPlugin(geometryutil) %>%
  registerPlugin(groupedlayercontrol) %>%
  # add basemap
  addProviderTiles(providers$CartoDB.Positron) %>%
  # set map boundaries
  fitBounds( grens[1], grens[2], grens[3], grens[4]) %>%
  onRender("function(el, x, data) {
    // read data from the named list passd to onRender
    // data.name_from_list
    var routes = data.routes;
    var groups = data.groups;
    var types = groups;
    
    // function to define line color based on 
    //  feature.properties.type
    function getColor(d) {
      return d == 'stremming' ? 'red' :
             d == 'omleiding' ? 'seagreen' :
                                'black';
    }
    // funciton to define line dash based on 
    //  feature.properties.type
    function getDash(d) {
      return d == 'stremming' ? '20' :
             d == 'omleiding' ? '' :
                                '';
    }
    // function to set style of polylines
    function newstyle(feature) {
      return {
          color: getColor(feature.properties.type),
          weight: 10,
          opacity: 1,
          dashArray: getDash(feature.properties.type),
          fillOpacity: 0.7
      };
    }
    
    // layerControl optioesn for groupedOverlays
    var options = {
      exclusiveGroups: ['Stremming'],
      groupCheckboxes: false,
      collapsed: false
    };
    // add empty layercontrol
    var layerControl = L.control.groupedLayers(null, null, options).addTo(this);
    
    // iterate over types, filter by that type, and format the layer for that feature type
    types.forEach(function(type) {
      var layer = L.geoJson(routes, {
            filter: function(feature, layer) {
              return feature.properties.groep == type;
            },
            style: newstyle,
            arrowheads: {frequency: 'endonly', yawn: 45, size: '30px', fill: true}
          })
          .on('mouseover', function (e) {e.target.setStyle({weight: 15, opacity: 1 });})
          .on('mouseout', function (e) {e.target.setStyle({weight: 10, opacity: 0.75});})
      
      // all done with the layer, add it to the control
      layerControl.addOverlay(layer, type, 'Stremming');
    });
    
    }", data = list(routes = sf_geojson(df), groups = bladen))