As said: it even differs by a factor of 2 in time! How is that possible? I found this issue but it seems it is still there?
It turns out that the highway is only taken in on direction (See leaflet map from map_route
. Do I miss anything?
Here is a reproducible example:
wd <- getwd()
setwd("C:/OSRM_API5")
shell(paste0("osrm-routed ", "switzerland-latest.osrm", " >nul 2>nul"), wait = F)
Sys.sleep(3) # OSRM needs time
setwd(wd)
k1 <- 46.99917
k2 <- 8.610048
k3 <- 47.05398
k4 <- 8.530232
r1 <- viaroute5_2(k1, k2, k3, k4)
r1$routes[[1]]$duration
# [1] 598.2
geometry <- decode_geom(r1$routes[[1]]$geometry, 5)
map_route(geometry)
r2 <- viaroute5_2(k3, k4,k1, k2)
r2$routes[[1]]$duration
# [1] 1302
geometry <- decode_geom(r2$routes[[1]]$geometry, 5)
map_route(geometry)
shell("TaskKill /F /IM osrm-routed.exe >nul 2>nul")
Here are the functions you need:
viaroute5_2 <- function(lat1, lng1, lat2, lng2) {
# address <- "http://localhost:5000" # this should work without a local server
address <- "http://localhost:5000"
request <- paste(address, "/route/v1/driving/",
lng1, ",", lat1, ";", lng2, ",", lat2,
"?overview=full", sep = "", NULL)
R.utils::withTimeout({
repeat {
res <- try(
route <- rjson::fromJSON(
file = request))
if (class(res) != "try-error") {
if (!is.null(res)) {
break
} else {
stop("???")
}
}
}
}, timeout = 1, onTimeout = "warning")
if (res$code == "Ok") {
return(res)
} else {
t_guess <- 16*60
warning("Route not found: ", paste(lat1, lng1, lat2, lng2, collapse = ", "),
". Time set to ", t_guess/60 , " min.")
}
}
decode_geom <- function(encoded, precision = stop("a numeric, either 5 or 6")) {
if (precision == 5) {
scale <- 1e-5
} else if (precision == 6) {
scale <- 1e-6
} else {
stop("precision not set to 5 or 6")
}
len = stringr::str_length(encoded)
encoded <- strsplit(encoded, NULL)[[1]]
index = 1
N <- 100000
df.index <- 1
array = matrix(nrow = N, ncol = 2)
lat <- dlat <- lng <- dlnt <- b <- shift <- result <- 0
while (index <= len) {
shift <- result <- 0
repeat {
b = as.integer(charToRaw(encoded[index])) - 63
index <- index + 1
result = bitops::bitOr(result, bitops::bitShiftL(bitops::bitAnd(b, 0x1f), shift))
shift = shift + 5
if (b < 0x20) break
}
dlat = ifelse(bitops::bitAnd(result, 1),
-(result - (bitops::bitShiftR(result, 1))),
bitops::bitShiftR(result, 1))
lat = lat + dlat;
shift <- result <- b <- 0
repeat {
b = as.integer(charToRaw(encoded[index])) - 63
index <- index + 1
result = bitops::bitOr(result, bitops::bitShiftL(bitops::bitAnd(b, 0x1f), shift))
shift = shift + 5
if (b < 0x20) break
}
dlng = ifelse(bitops::bitAnd(result, 1),
-(result - (bitops::bitShiftR(result, 1))),
bitops::bitShiftR(result, 1))
lng = lng + dlng
array[df.index,] <- c(lat = lat * scale, lng = lng * scale)
df.index <- df.index + 1
}
geometry <- data.frame(array[1:df.index - 1,])
names(geometry) <- c("lat", "lng")
return(geometry)
}
map <- function() {
library(leaflet)
m <- leaflet() %>%
addTiles() %>%
addProviderTiles(providers$OpenStreetMap, group = "OSM") %>%
addProviderTiles(providers$Stamen.TonerLite, group = "Toner Lite") %>%
addLayersControl(baseGroups = c("OSM", "Toner Lite"))
return(m)
}
map_route <- function(geometry) { # Which parameters make sence? osrm inside or outside?
m <- map()
m <- addCircleMarkers(map = m,
lat = geometry$lat[1],
lng = geometry$lng[1],
color = imsbasics::fhs(),
popup = paste("Source"),
stroke = FALSE,
radius = 6,
fillOpacity = 0.8) %>%
addCircleMarkers(lat = geometry$lat[nrow(geometry)],
lng = geometry$lng[nrow(geometry)],
color = imsbasics::fhs(),
popup = paste("Destination"),
stroke = FALSE,
radius = 6,
fillOpacity = 0.8) %>%
addPolylines(lat = geometry$lat, lng = geometry$lng, color = "red", weight = 4) %>%
addLayersControl(baseGroups = c("OSM", "Stamen.TonerLite"))
return(m)
}
The answer is: Because OSRM searches by default a nearest point and searches one route from that point. If your coordinates are slightly north a highway, OSRM will only drive westbound (considering you're drive on the right side as we do in Europe..).
So in your example the point upleft is just a bit north of the highway and therefore when searching from that point OSRM takes quite a bit of a detour.
The following example shows this:
osrmr::run_server("switzerland-latest", "C:/OSRM_API5")
lat1 <- 46.99917
lng1 <- 8.610048
lat2 <- 47.05398
lng2 <- 8.530232
res1 <- osrmr::viaroute(lat1, lng1, lat2, lng2, instructions = TRUE, api_version = 5, localhost = TRUE)
res2 <- osrmr::viaroute(lat2, lng2, lat1, lng1, instructions = TRUE, api_version = 5, localhost = TRUE)
res1$routes[[1]]$duration
# [1] 598.2
res2$routes[[1]]$duration
# [1] 1302
map_route(decode_geom(res1$routes[[1]]$geometry, 5))
map_route(decode_geom(res2$routes[[1]]$geometry, 5))
lat1 <- 46.99917
lng1 <- 8.610048
lat2 <- 47.051 # setting that point a bit more south changes the results to the opposite..
lng2 <- 8.530232
res1 <- osrmr::viaroute(lat1, lng1, lat2, lng2, instructions = TRUE, api_version = 5, localhost = TRUE)
res2 <- osrmr::viaroute(lat2, lng2, lat1, lng1, instructions = TRUE, api_version = 5, localhost = TRUE)
res1$routes[[1]]$duration
# [1] 1307.5
res2$routes[[1]]$duration
# [1] 592.7
map_route(decode_geom(res1$routes[[1]]$geometry, 5))
map_route(decode_geom(res2$routes[[1]]$geometry, 5))
osrmr::quit_server()
As you can see, setting the second point a bit more south inverts the results. Now the other way takes quite a bit longer.
As discussed for example here the radiuses
option might provide a solution to that problem. I couldn't however figure out to get that to work on your example..
Or maybe (simpler..) you want to calculate both directions and take the shorter duration
?
What's best really depends on your algorithmic problem..