RoyalTS
RoyalTS

Reputation: 10203

leaflet plugin and leafletProxy

I've added the leaflet TextPath plugin to a shiny app in analogy to this example. This works pretty well:

output$fullscreen_map <- renderLeaflet({
  points <- points_reactive()

  points %>%
    leaflet() %>% 
    addTiles() %>%
    fitBounds(~min(lon), ~min(lat), ~max(lon), ~max(lat)) %>%
    registerPlugin(textPathPlugin) %>%
    onRender("function(el, x, data) {
                data = HTMLWidgets.dataframeToD3(data);
                data = data.map(function(val) { return [val.lat, val.lon]; });
                var layer = L.polyline(data);
                layer.on('mouseover', function () {
                this.setText('  ►  ', {repeat: true, attributes: {fill: 'blue'}});
                });
                layer.on('mouseout', function () {
                this.setText(null);
                });
                layer.addTo(this);
    }", data = points)

})

In accordance with the docs I would now like to use leafletProxy() so that the entire map doesn't get redrawn whenever the reactive data source changes. Alas, using the following snippet of code

leafletProxy("fullscreen_map", data = points) %>%
  onRender("function(el, x, data) {
              data = HTMLWidgets.dataframeToD3(data);
              data = data.map(function(val) { return [val.lat, val.lon]; });
              var layer = L.polyline(data);
              layer.on('mouseover', function () {
              this.setText('  ►  ', {repeat: true, attributes: {fill: 'blue'}});
              });
              layer.on('mouseout', function () {
              this.setText(null);
              });
              layer.addTo(this);
  }", data = points)

does not work as intended. I assume this is because onRender is only called when a new render occurs and the whole point of leafletProxy is to not re-render? If this is correct, is there a way to do this using other methods?

Upvotes: 11

Views: 1147

Answers (2)

yosukesabai
yosukesabai

Reputation: 6244

It could be something like below, although there would be more cleaner methods.

What I did was to use leafletProxy to add polylines layer from points_reactive() function, while setting group to be reactive. I used listened to layeradd event of map, and if a layer with reactive group got added, i added the textPath.

output$fullscreen_map <- renderLeaflet({
  points <- points_reactive()

  points %>%
    leaflet() %>% 
    addTiles() %>%
    fitBounds(~min(lon), ~min(lat), ~max(lon), ~max(lat)) %>%
    registerPlugin(textPathPlugin) %>%
    onRender("function(el, x, data) {
              var mymap = this;

              mymap.on('layeradd',
                function(e) {
                  console.log(e);
                  var layer = e.layer;

                  if (layer.groupname == 'reactive') {
                    layer.on('mouseover', function () {
                      this.setText('  ►  ', {repeat: true, attributes: {fill: 'blue'}});
                    });
                    layer.on('mouseout', function () {
                      this.setText(null);
                    });
                  }
                }
              );

            }", data = points)

  })

  observeEvent(input$clickme,{
               points <- points_reactive()

               leafletProxy('fullscreen_map') %>%
                 addPolylines(lng=points$lon, lat=points$lat, group='reactive')

  }
)
}

Upvotes: 1

dagelf
dagelf

Reputation: 1739

The docs say: active inputs and expressions that affect the renderLeaflet expression will cause the entire map to be redrawn from scratch and reset the map position and zoom level.

It sounds like you will have to dig deeper into Leaflet or trigger leafletProxy yourself, and make sure that the data source does not change, by using two data sources... (Perhaps "Proxy" is a misnomer here.)

Have a look and see if one of these plugins can help: https://leafletjs.com/plugins#dynamiccustom-data-loading

Upvotes: 0

Related Questions