Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add polylines from one location to others separately using leaflet in shiny?

Tags:

r

leaflet

shiny

I'm trying to add polylines from one specific location to many others in shiny R using addPolylines from leaflet. But instead of linking from one location to the others, I am only able to link them all together in a sequence. The best example of what I'm trying to achieve is seen here in the cricket wagon wheel diagram: .

observe({
  long.path <- c(-73.993438700, (locations$Long[1:9]))
  lat.path <- c(40.750545000, (locations$Lat[1:9]))
  proxy <- leafletProxy("map", data = locations)
  if (input$paths) {
     proxy %>% addPolylines(lng = long.path, lat = lat.path, weight = 3, fillOpacity = 0.5, 
                       layerId = ~locations, color = "red")
  }
})

It is in a reactive expression as I want them to be activated by a checkbox.

I'd really appreciate any help with this!

like image 349
jdillo12 Avatar asked Jul 09 '15 07:07

jdillo12


3 Answers

Note

I'm aware the OP asked for a leaflet answer. But this question piqued my interest to seek an alternative solution, so here are two


Example - mapdeck

Mapdeck (my package) uses Deck.gl on a Mapbox map, so you need a Mapbox API key to use it. But it does let you plot 2.5d arcs

It works on data.frames and data.tables (as well as sp and sf) objects.

center <- c(144.983546, -37.820077)

df_hits$center_lon <- center[1]
df_hits$center_lat <- center[2]

df_hits$score <- sample(c(1:4,6), size = nrow(df_hits), replace = T)

library(mapdeck)

set_token("MAPBOX")

mapdeck(
  style = mapdeck_style("satellite")
) %>%
  add_arc(
    data = df_hits
    , origin = c("center_lon", "center_lat")
    , destination = c("lon", "lat")
    , stroke_from = "score"
    , stroke_to = "score"
    , stroke_width = "score"
    , palette = "magma"
  )

enter image description here

Example - googleway

This example uses googleway (also my package, which interfaces Google Maps API), and also works on data.frames and data.tables (as well as sp and sf)

The trick is in the encodeCoordinates function, which encodes coordinates (lines) into a Google Polyline

library(data.table)
library(googleway)
library(googlePolylines) ## gets installed when you install googleway

center <- c(144.983546, -37.820077)

setDT(df_hits)  ## data given at the end of the post

## generate a 'hit' id
df_hits[, hit := .I]

## generate a random score for each hit
df_hits[, score := sample(c(1:4,6), size = .N, replace = T)]

df_hits[
    , polyline := encodeCoordinates(c(lon, center[1]), c(lat, center[2]))
    , by = hit
]

set_key("GOOGLE_MAP_KEY") ## you need an API key to load the map

google_map() %>%
    add_polylines(
        data = df_hits
        , polyline = "polyline"
        , stroke_colour = "score"
        , stroke_weight = "score"
        , palette = viridisLite::plasma
    )

enter image description here


The dplyr equivalent would be

df_hits %>%
    mutate(hit = row_number(), score = sample(c(1:4,6), size = n(), replace = T)) %>%
    group_by(hit, score) %>%
    mutate(
        polyline = encodeCoordinates(c(lon, center[1]), c(lat, center[2]))
    )

Data

df_hits <- structure(list(lon = c(144.982933659011, 144.983487725258, 
144.982804912978, 144.982869285995, 144.982686895782, 144.983239430839, 
144.983293075019, 144.983529109412, 144.98375441497, 144.984103102141, 
144.984376687461, 144.984183568412, 144.984344500953, 144.984097737723, 
144.984065551215, 144.984339136535, 144.984001178199, 144.984124559814, 
144.984280127936, 144.983990449363, 144.984253305846, 144.983030218536, 
144.982896108085, 144.984022635871, 144.983786601478, 144.983668584281, 
144.983673948699, 144.983577389175, 144.983416456634, 144.983577389175, 
144.983282346183, 144.983244795257, 144.98315360015, 144.982896108085, 
144.982686895782, 144.982617158347, 144.982761997634, 144.982740539962, 
144.982837099486, 144.984033364707, 144.984494704658, 144.984146017486, 
144.984205026084), lat = c(-37.8202049841516, -37.8201201023877, 
-37.8199253045246, -37.8197812267274, -37.8197727515541, -37.8195269711051, 
-37.8197600387923, -37.8193828925304, -37.8196964749506, -37.8196583366193, 
-37.8195820598976, -37.8198956414717, -37.8200651444706, -37.8203575362288, 
-37.820196509027, -37.8201032825917, -37.8200948074554, -37.8199253045246, 
-37.8197897018997, -37.8196668118057, -37.8200566693299, -37.8203829615443, 
-37.8204295746001, -37.8205355132537, -37.8194761198756, -37.8194040805737, 
-37.819569347103, -37.8197007125418, -37.8196752869912, -37.8195015454947, 
-37.8194930702893, -37.8196286734591, -37.8197558012046, -37.8198066522414, 
-37.8198151274109, -37.8199549675656, -37.8199253045246, -37.8196964749506, 
-37.8195862974953, -37.8205143255351, -37.8200270063298, -37.8197430884399, 
-37.8195354463066)), row.names = c(NA, -43L), class = "data.frame")
like image 200
SymbolixAU Avatar answered Oct 08 '22 18:10

SymbolixAU


I know this was asked a year ago but I had the same question and figured out how to do it in leaflet.

You are first going to have to adjust your dataframe because addPolyline just connects all the coordinates in a sequence. It seems that you know your starting location and want it to branch out to 9 separate locations. I am going to start with your ending locations. Since you have not provided it, I will make a dataframe with 4 separate ending locations for the purpose of this demonstration.

dest_df <- data.frame (lat = c(41.82, 46.88, 41.48, 39.14),
                   lon = c(-88.32, -124.10, -88.33, -114.90)
                  )

Next, I am going to create a data frame with the central location of the same size (4 in this example) of the destination locations. I will use your original coordinates. I will explain why I'm doing this soon

orig_df <- data.frame (lat = c(rep.int(40.75, nrow(dest_df))),
                   long = c(rep.int(-73.99,nrow(dest_df)))
                  )

The reason why I am doing this is because the addPolylines feature will connect all the coordinates in a sequence. The way to get around this in order to create the image you described is by starting at the starting point, then going to destination point, and then back to the starting point, and then to the next destination point. In order to create the dataframe to do this, we will have to interlace the two dataframes by placing in rows as such:

starting point - destination point 1 - starting point - destination point 2 - and so forth...

The way I will do is create a key for both data frames. For the origin dataframe, I will start at 1, and increment by 2 (e.g., 1 3 5 7). For the destination dataframe, I will start at 2 and increment by 2 (e.g., 2, 4, 6, 8). I will then combine the 2 dataframes using a UNION all. I will then sort by my sequence to make every other row the starting point. I am going to use sqldf for this because that is what I'm comfortable with. There may be a more efficient way.

orig_df$sequence <- c(sequence = seq(1, length.out = nrow(orig_df), by=2))
dest_df$sequence <- c(sequence = seq(2, length.out = nrow(orig_df), by=2))

library("sqldf")
q <- "
SELECT * FROM orig_df
UNION ALL
SELECT * FROM dest_df
ORDER BY sequence
"
poly_df <- sqldf(q)

The new dataframe looks like this (notice how the origin locations are interwoven between the destination):

SS of data

And finally, you can make your map:

library("leaflet")
leaflet() %>%
  addTiles() %>%

  addPolylines(
    data = poly_df,
    lng = ~lon, 
    lat = ~lat,
    weight = 3,
    opacity = 3
  ) 

And finally it should look like this:

SS of Leaflet Map

I hope this helps anyone who is looking to do something like this in the future

like image 24
David Sung Avatar answered Oct 08 '22 17:10

David Sung


Here is a possible approach based on the mapview package. Simply create SpatialLines connecting your start point with each of the end points (stored in locations), bind them together and display the data using mapview.

library(mapview)
library(raster)

## start point
root <- matrix(c(-73.993438700, 40.750545000), ncol = 2)
colnames(root) <- c("Long", "Lat")

## end points
locations <- data.frame(Long = (-78):(-70), Lat = c(40:44, 43:40))

## create and append spatial lines
lst <- lapply(1:nrow(locations), function(i) {
  SpatialLines(list(Lines(list(Line(rbind(root, locations[i, ]))), ID = i)), 
               proj4string = CRS("+init=epsg:4326"))
})

sln <- do.call("bind", lst)

## display data
mapview(sln)

lines

Just don't get confused by the Line-to-SpatialLines procedure (see ?Line, ?SpatialLines).

like image 4
fdetsch Avatar answered Oct 08 '22 18:10

fdetsch