OSRM: Why is the traveltime of route A -> B a fact

2019-07-08 03:24发布

问题:

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

回答1:

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



标签: r osrm