I made the following map in R (from a data frame with 5 points ordered from "1" to "5"):
library(dplyr)
library(leaflet)
map_data <- data.frame("Lat" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), "Long" = c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), type = c(1,2,3,4,5))
map_data$type = as.factor(map_data$type)
leaflet(map_data) %>% addTiles() %>% addCircleMarkers(stroke = FALSE, label = ~type,
labelOptions = labelOptions(noHide = TRUE, offset=c(0,-12), fill = TRUE, opacity = 10, weight = 10, textOnly = TRUE))
But the problem is, you can barely see the "labels" for each city:
I would like to make the "labels" a lot more noticeable, something like this:
I tried to play around with the "weight" and "opacity" arguments, but this does not seem to be working.
Can someone please show me how to do this?
Note: I do not want to do this in R SHINY, just using LEAFLET in R.
Reference: Print label on circle markers in leaflet in Rshiny
fillOpacity = before labelOptions:
library(dplyr)
library(leaflet)
map_data <- data.frame("Lat" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), "Long" = c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), type = c(1,2,3,4,5))
map_data$type = as.factor(map_data$type)
leaflet(map_data) %>%
addTiles() %>%
addCircleMarkers(stroke = FALSE,
label = ~type,
fillOpacity = 0.9,
labelOptions = labelOptions(noHide = TRUE, fill = FALSE, offset = c(5,2), textsize = "18px", textOnly = TRUE))
I am working with the R programming language.
Using the "leaflet" library, I made the following map for these 5 cities:
library(dplyr)
library(leaflet)
map_data <- data.frame("Lat" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), "Long" = c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), type = c(1,2,3,4,5))
map_data$type = as.factor(map_data$type)
leaflet(map_data) %>%
addTiles() %>% addCircleMarkers(stroke = FALSE, label = ~type,fillOpacity = 0.8, labelOptions = labelOptions(direction = "center",style = list('color' = "white"),noHide = TRUE, offset=c(0,0), fill = TRUE, opacity = 1, weight = 10, textOnly = TRUE))
On this above map that I have created, I would now like to "connect" all these "points" (i.e. cities) on the map (in a route) based on their "number" (e.g. connect 1 with 2, 2 with 3, 3 with 4, 4 with 5, 5 with 1), and output the "total distance" of the route. I found a previous post that shows how to do this: How to show path and distance on map with leaflet, shiny apps?
I tried to adapt the code from this post to suit my question:
library(osrm)
route = osrmRoute(c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), overview = 'full')
route_summary = osrmRoute(c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), overview = FALSE)
leaflet() %>% addTiles() %>%
addCircleMarkers(c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957 ), c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629), stroke = FALSE, label = ~type,fillOpacity = 0.8,
labelOptions = labelOptions(direction = "center",style = list('color' = "white"),noHide = TRUE, offset=c(0,0), fill = TRUE, opacity = 1, weight = 10, textOnly = TRUE)) %>%
addPolylines(route$lon,route$lat,
label = paste(round(route_summary[1]/60), 'hr - ', round(route_summary[2]), 'km'),
labelOptions = labelOptions(noHide = TRUE))
But this returns the following error:
Error in UseMethod("metaData") :
no applicable method for 'metaData' applied to an object of class "NULL"
Can someone please show me how to fix this problem?
I would like to do this using "leaflet" and not using "rshiny". In the end, I would like the final map to look something like this (this is supposed to represent a "single path" from a Travelling Salesman Problem) :
[![enter image description here][2]][2]
Note: I am starting to think that problem might be that the "osrmRoute()" function might not be able to work for more than 2 points?
https://github.com/riatelab/osrm/issues/41
https://rdrr.io/cran/osrm/man/osrmTrip.html
One way is for you to make API call:
https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md
I'll just outline how can you do it:
data
df <- data.frame(
lon = c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957),
lat = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629)
)
url call
root <- "http://router.project-osrm.org/route/v1/driving/"
options <- c(
continue_straight = "true",
overview = "full",
annotations = "true",
steps = "true"
)
coords <- df %>%
slice(c(seq_len(n()), 1)) %>%
pmap_chr(str_c, sep = ",") %>% str_c(collapse = ";")
options <- options %>%
imap_chr(~str_c(.y, "=", .x)) %>%
str_c(collapse = "&") %>%
str_c("?", .)
res <- rjson::fromJSON(file = str_c(root, coords, options))
Note that I've added first point as 6th row to make circle route.
map
res$routes[[1]]$geometry %>%
googlePolylines::decode() %>%
.[[1]] %>%
leaflet() %>%
addTiles() %>%
addPolylines(lng = ~lon, lat = ~lat) %>%
addCircleMarkers(
data = df,
stroke = FALSE,
label = seq_len(nrow(df)),
fillOpacity = 0.8,
labelOptions = labelOptions(
direction = "center",
style = list('color' = "white"),
noHide = TRUE,
offset=c(0,0),
fill = TRUE,
opacity = 1,
weight = 10,
textOnly = TRUE
)
)
distance
res$routes[[1]]$distance
This is in meters (documentation)
EDIT
There probably is better way of labeling polyline but I don't have time now:
library(sf)
segment_df <- df %>% rbind(df[1,])
d <- segment_df %>%
st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
{st_distance(.[-6,], .[-1,], by_element = TRUE)} %>%
as.vector() %>%
round()
m <- leaflet() %>% addTiles()
for(i in seq_len(nrow(segment_df) - 1))
m <- m %>% addPolylines(
data = segment_df[i:(i+1),],
lng = ~lon, lat = ~lat, color = "red", label = paste(d[[i]], "m"),
labelOptions(noHide = TRUE, direction = 'top')
)
m <- m %>% addCircleMarkers(
data = df,
stroke = FALSE,
label = seq_len(nrow(df)),
fillOpacity = 0.8,
labelOptions = labelOptions(
direction = "center",
style = list('color' = "white"),
noHide = TRUE,
offset=c(0,0),
fill = TRUE,
opacity = 1,
weight = 10,
textOnly = TRUE
)
)
If you want only to show total distance then that is easier and does not require loop, just replace loop with:
segment_df %>%
leaflet() %>%
addTiles() %>%
addPolylines(
lng = ~lon, lat = ~lat, color = "red",
label = paste(sum(d), "m"),
labelOptions = labelOptions(noHide = TRUE, direction = 'top')
)
I hope you understand (and see from map) that this is not drivable.
Here is an answer I tried based on #det's answer:
library(sf)
library(geosphere)
library(dplyr)
library(leaflet)
library(data.table)
library(VPF)
#add a 6th row that is equal to the 1st row - so that the path loops back
map_data <- data.frame("Lat" = c(43.6426, 43.6424, 43.6544, 43.6452, 43.6629, 43.6426), "Long" = c(-79.3871, -79.3860, -79.3807, -79.3806,-79.3957, -79.3871 ), type = c(1,2,3,4,5,1))
map_data$type = as.factor(map_data$type)
m1 = leaflet(map_data) %>% addTiles() %>% addCircleMarkers(stroke = FALSE, label = ~type,fillOpacity = 0.8,
color = ~ifelse(type==1,"red","blue"), labelOptions = labelOptions(direction = "center",style = list('color' = "white"),
noHide = TRUE, offset=c(0,0), fill = TRUE, opacity = 1, weight = 10, textOnly = TRUE))
m1 %>% addTiles() %>%
addPolylines(data = map_data, lng = ~Long, lat = ~Lat, group = ~type)
Now, I want to calculate the total distance of the trip and have it displayed on the map:
#distances (https://stackoverflow.com/questions/42119438/calculate-distance-between-two-long-lat-coordinates-in-a-dataframe)
result = rbind(
cbind(map_data[1:nrow(map_data)-1,c(1,2)], map_data[-1,c(1,2)]),
cbind(map_data[nrow(map_data), c(1,2)], map_data[1,c(1,2)])
)
colnames(result) <- c("start_lat", "start_long", "end_lat", "end_long")
result$id = as.factor(c(1,2,3,4,5,1))
result = data.frame(result)
for (i in 1:nrow(result)) {
a<-result$start_long[i]
b<-result$start_lat[i]
c<-result$end_long[i]
d<-result$end_lat[i]
result$distance[i]<-distm(c(a,b),c(c,d), fun = distHaversine)
}
#total distance of trip in meters
d = result$distance
total_d = signif(sum(d),3)
m1 %>% addPolylines(
data = map_data,
lng = ~Long, lat = ~Lat, color = "blue", label = paste0(total_d, " meters"),
labelOptions(noHide = TRUE, direction = 'top')
)
I think I finally got it - thanks so much # Det!
I'm trying to create a choropleth map in county level using leaflet R package and I actually do it. But when I check my data file and the hover text of any county I find that the values are not correct. For example check the Conejos county. Any explanation? Or a better way to process tha data and create this map without the mismatches?
Code:
library(raster)
library(leaflet)
library(tidyverse)
# Get USA polygon data
USA <- getData("GADM", country = "usa", level = 2)
### Get data
mydata <- read.csv("https://www.betydb.org/miscanthus_county_avg_yield.csv",
stringsAsFactors = FALSE) %>%
dplyr::select(COUNTY_NAME, Avg_yield)
### Check counties that exist in USA, but not in mydata
### Create a dummy data frame and bind it with mydata
mydata <- data.frame(COUNTY_NAME = setdiff(USA$NAME_2, mydata$COUNTY_NAME),
Avg_yield = NA,
stringsAsFactors = FALSE) %>%
bind_rows(mydata)
### Create a color palette
mypal <- colorNumeric(palette = "viridis", domain = mydata$Avg_yield)
leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
setView(lat = 39.8283, lng = -98.5795, zoom = 4) %>%
addPolygons(data = USA, stroke = FALSE, smoothFactor = 0.2, fillOpacity = 0.3,
fillColor = ~mypal(mydata$Avg_yield),
popup = paste("Region: ", USA$NAME_2, "<br>",
"Avg_yield: ", mydata$Avg_yield, "<br>")) %>%
addLegend(position = "bottomleft", pal = mypal, values = mydata$Avg_yield,
title = "Avg_yield",
opacity = 1)
Using the renderLeaflet (following codes), I am trying to create an interactive map within shiny. I want the map to be updated based on users selections of age group, sex, and year. When age group=60 and sex=both sexes and year= 2010 are selected, everything looks great (please see this screen shot).
However, when the other age groups are selected, the legend remains unchanged and is not rendered (Please see the second screen shot).
Here is my code:
mapdata_ <- reactive ({
nhmap$Per <- round(nhmap$Per, 1)
out_map <- nhmap %>%
filter (
Age_Group %in% input$Age_Group_map,
Sex %in% input$sex_map,
Year %in% input$Year_map)
return(out_map)
})
output$int_map <- renderLeaflet ({
leaflet (mapdata_(),
pal8 <- c("#FFFFE5", "#D9F0A3", "#78C679", "#006837") ,
pal <- colorBin(palette = pal8, domain = nhmap$Per, bins=4, right =FALSE, na.color = "#808080", alpha = FALSE, reverse = F)
) %>%
addProviderTiles("CartoDB.Positron") %>%
clearControls() %>%
clearShapes()%>%
addPolygons(fillColor = ~pal(Per),
stroke=T,
weight=1,
smoothFactor=0.2,
fillOpacity = 1,
color="black",
popup=~paste(NAME,"<br>",input$sex_map,
input$Age_Group_map,"=",Per,"%"),
highlightOptions = highlightOptions(color = "red",
weight = T,
bringToFront = T),
label=~NAME) %>%
addTiles() %>%
setView(-82.706838, 40.358615, zoom=7) %>%
addLegend(position = "bottomright",
values = ~pal(Per),
pal = pal,
title = (paste("%",input$Age_Group_map, input$sex_map, "in", input$Year_map)) ,
labFormat = labelFormat())
})
I am trying to write a script that would allow people to click on a country then a popup would appear and show the country's name and corresponding value. I have successfully created the map and the boundaries of each country, however when you click on, for example Russia, the popup would show India. Below is a simplified version of the script:
library(leaflet)
library(maps)
countries <- c("Australia", "South Africa", "India", "Mexico", "USA", "Russia")
values <- c(1,2,3,4,5,6)
bounds <- map("world", countries, fill = TRUE, plot = FALSE)
map <- leaflet(data) %>%
addTiles() %>%
addPolygons(data = bounds, group = "Countries",
color = "red",
weight = 2,
popup = paste("Country: ", countries, "<br>", "Value: ", values, "<br>"),
fillOpacity = 0.1,
highlightOptions = highlightOptions(color = "black",
weight = 2,
bringToFront = TRUE))
map
The map is generated using the leaflet package in R. Any solutions or advice would be welcome, I am sure it is a simple error that I am making somewhere.
I leave two options for you. If you just want to show the country names as they are in bounds (e.g., Australia:Melville Island), you need to change popup = countries to popup = ~names.
map1 <- leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addPolygons(data = bounds, group = "Countries",
color = "red",
weight = 2,
popup = ~names,
fillOpacity = 0.1,
highlightOptions = highlightOptions(color = "black",
weight = 2,
bringToFront = TRUE))
If you want to just have the country names as you specified in countries, you want to manipulate names using gsub(), for example. Here I removed all characters from : to the end for each name.
map2 <- leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addPolygons(data = bounds, group = "Countries",
color = "red",
weight = 2,
popup = ~gsub(x = names, pattern = ":.*$", replacement = ""),
fillOpacity = 0.1,
highlightOptions = highlightOptions(color = "black",
weight = 2,
bringToFront = TRUE))
EXTRA
The OP added one more thing to his question. Here is my idea. If you have two things to show in popups, you can do the following. You add value to bounds and create popups.
# Add values to bounds.
set.seed(111)
bounds$value <- sample.int(n = 1000, size = 301, replace = TRUE)
map3 <- leaflet() %>%
addProviderTiles("OpenStreetMap.Mapnik") %>%
addPolygons(data = bounds, group = "Countries",
color = "red",
weight = 2,
popup = paste("Country: ", bounds$names, "<br>",
"Value: ", bounds$value, "<br>"),
fillOpacity = 0.1,
highlightOptions = highlightOptions(color = "black",
weight = 2,
bringToFront = TRUE))