Friday, June 8, 2018

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

1 comment

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!

3 Answers

Answers 1

Note

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


Example

Other answers indicate you need to create an individual line for each from/to coordinate pair. In this case you need separate lines for each of the lines going from the center to the outer points.

Here's an example using googleway (my package, which interfaces Google Maps API), and works on data.frames and data.tables (as per this example), rather than spatial (sp or sf) objects.

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") 

Answers 2

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).

Answers 3

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

If You Enjoyed This, Take 5 Seconds To Share It

1 comment: