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