OSRM:为什么路线 A -> B 的旅行时间是路线旅行时间 B -> A 的 2 倍?
OSRM: Why is the traveltime of route A -> B a factor 2 off route's traveltime B -> A?
如前所述:时间甚至相差2倍!这怎么可能?
我找到了这个 issue 但它似乎还在那里?
原来高速公路只是顺方向走的(见map_route
的传单地图。有没有漏掉什么?
这是一个可重现的例子:
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")
这是您需要的功能:
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)
}
答案是:因为OSRM默认搜索最近的点并从该点搜索一条路线。如果您的坐标稍微偏北高速公路,OSRM 只会向西行驶(考虑到您像我们在欧洲一样靠右侧行驶......)。
所以在你的例子中,upleft 点就在高速公路的北边一点,因此当从那个点搜索时,OSRM 需要绕很多弯路。
以下示例说明了这一点:
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()
如您所见,将第二个点设置得更靠南一点会反转结果。现在另一种方式需要更长的时间。
如所讨论的,例如 here,radiuses
选项可能会提供该问题的解决方案。但是我无法弄清楚如何让它在你的例子中起作用..
或者(更简单..)您想要计算两个方向并取较短的 duration
?
什么是最好的真的取决于你的算法问题..
如前所述:时间甚至相差2倍!这怎么可能? 我找到了这个 issue 但它似乎还在那里?
原来高速公路只是顺方向走的(见map_route
的传单地图。有没有漏掉什么?
这是一个可重现的例子:
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")
这是您需要的功能:
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)
}
答案是:因为OSRM默认搜索最近的点并从该点搜索一条路线。如果您的坐标稍微偏北高速公路,OSRM 只会向西行驶(考虑到您像我们在欧洲一样靠右侧行驶......)。
所以在你的例子中,upleft 点就在高速公路的北边一点,因此当从那个点搜索时,OSRM 需要绕很多弯路。
以下示例说明了这一点:
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()
如您所见,将第二个点设置得更靠南一点会反转结果。现在另一种方式需要更长的时间。
如所讨论的,例如 here,radiuses
选项可能会提供该问题的解决方案。但是我无法弄清楚如何让它在你的例子中起作用..
或者(更简单..)您想要计算两个方向并取较短的 duration
?
什么是最好的真的取决于你的算法问题..