SymbolixAU / mapdeck

R interface to Deck.gl and Mapbox
https://symbolixau.github.io/mapdeck/articles/mapdeck.html
362 stars 40 forks source link

Adding a navigationControl #328

Open ifellows opened 4 years ago

ifellows commented 4 years ago

Some of the users of my Shiny app don't know how to zoom when given a map. Is there any possible way to add a navigationControl to the map?

I tried:

    mapdeck(
      token = key,
      style = 'mapbox://styles/mapbox/dark-v10'
    ) %>%
      htmlwidgets::onRender(
        "function(el, x){console.log('.');nav = new mapboxgl.NavigationControl();mapmap._map.getMap().addControl(nav, 'top-left');}"
      )

This adds the control, but any scatterplots or other layers don't render properly on use of the control.

dcooley commented 4 years ago

In this line

mapmap._map.getMap()

How do you get / define mapmap?

ifellows commented 4 years ago

My shiny output is defined as output$map. I was just looking around in the javascript console for a way to get to the mapbox map object, and found mapmap.

dcooley commented 4 years ago

oh it's in a shiny.

Full reprex of the issue.


library(shiny)
library(shinydashboard)
library(mapdeck)
set_token( secret::get_secret("MAPBOX") )

ui <- dashboardPage(
  header = dashboardHeader()
  , sidebar = dashboardSidebar()
  , body = dashboardBody(
    mapdeck::mapdeckOutput(
      outputId = "map"
    )
  )
)

server <- function( input, output ) {

  output$map <- mapdeck::renderMapdeck({
    mapdeck(
      style = 'mapbox://styles/mapbox/dark-v10'
    ) %>%
    htmlwidgets::onRender(
      "function(el, x){console.log('.');nav = new mapboxgl.NavigationControl();mapmap._map.getMap().addControl(nav, 'top-left');}"
    ) %>%
    add_scatterplot(
      data = roads
    )
  })

}

shinyApp(ui, server)

The layers get 'stuck' when using the control, and only 'refresh' when interacting with the map. I've seen this behaviour somewhere before, but can't quite remember where....

crazycapivara commented 4 years ago

@ifellows This does not work because the deck.gl layers and the mapbox basemap are not in the same WebGL context and therefore are not synchronize automatically. deckgl-and-mapbox-better-together and mapbox-deck.gl-API show how deck.gl layers can be added to the mapboxgl context with custom layers. Then you can add any mapbox control, popups etc. to the deck.gl layer.

ifellows commented 4 years ago

@crazycapivara Is it possible to link the layers from the mapdeck users perspective, or would this require a feature addition to mapdeck itself?

crazycapivara commented 4 years ago

@ifellows Therefore, it needs a mapbox widget to which mapdeck layers could be added, but the syntax for adding a deck layer to a mapboxgl.map object is slighty different.

You can take a look at the low level interface r2deck, where you must write your vizualisation function in pure JavaScript. You can either use the mapbox WebGL context with r2mapbox() or the deck.gl WebGL context with r2deck().

To move the Getting Started example to the mapbox context, where you can add a navigation control, it would look like this:

// arc-layer.js

function r2deckViz(map, data, options) {
  const arcLayer = new deck.MapboxLayer({
    id: "arc-layer",
    type: deck.ArcLayer,
    data: data,
    getSourcePosition: data => [data.start_lon, data.start_lat],
    getTargetPosition: data => [data.end_lon, data.end_lat],
    getSourceColor: [64, 255, 0],
    getTargetColor: [0, 128, 200]
  });

  map.on("load", () => {
    map.addControl(new mapboxgl.NavigationControl());
    map.addLayer(arcLayer);
  });
}
library(r2deck)

data_url <- paste0(
  "https://raw.githubusercontent.com/plotly/datasets/master/",
  "2011_february_aa_flight_paths.csv"
)
flights <- data.table::fread(data_url)
head(flights)

r2mapbox(
  script = "arc-layer.js",
  data = flights,
  lng = -87.6500532,
  lat = 41.850033,
  zoom = 2,
  pitch = 45
)