Working with the OSRM API

Code
Analysis
GIS
R
Routing
Open Street Map (osrm)
In this blog post, I would demonstrate how I used the osrm package in R to return the distance and travel time between a destination and different sources
Author

Olumide Oyalola

Published

October 19, 2022

Introduction to OSRM R package

An interface between R and the OSRM (OpenStreetMap-Based Routing Service) API. OSRM is a routing service based on OpenStreetMap data. See for more information. This package allows to compute routes, trips, isochrones and travel distances matrices (travel time in minutes and distance in kilometer).

The package is available on CRAN and it can be installed by running the code chunk below from the Rstudio console.

For the purpose of this post, the osrmTable function from the osrm package in R would be used to return the distance in meter and the travel time in minutes.

Code
#install.packages("osrm")

Load Packages

Code
# Install pacman package if needed
#if(!require("pacman")) install.packages("pacman")

# load the required packages

pacman::p_load(
  httr,
  jsonlite,
  tidyjson,
  tidyverse,
  lubridate,
  geosphere,
  anytime,
  tictoc,
  stringi,
  maptools,
  geosphere,
  sf,
  sp,
  openxlsx,
  leaflet,
  magrittr,
  janitor,
  arrow,
  osrm
)

Load Data

Code
# load  file as tibble

tic("Load the csv file")

df <- read_parquet("ml_data.parquet")

toc()
Load the csv file: 0.18 sec elapsed

Data Munging

Code
remove <- c("/Date", "(", ")/", "[[:punct:]]")

var <- c("ActualSpeed", "Address", "Altitude", "AssetClass", "AssetLocationID", "AssetStatus", "CategoryID", "CategoryName", "City", "CustomerName", "DateTimeLocal", "DateTimeReceived", "DeliveryOrderNumber", "DepartureDateTime", "DestinationArea", "DestinationCity", "DestinationSite", "DestinationStreet", "DeviceType", "DirectionString", "Distance", "DriverCode", "DriverID", "Geofence", "IgnitionStatus", "Information", "JourneyDistance", "JourneyDuration", "JourneyIdleTime", "JourneyMaxSpeed", "LastIgnitionOff", "LastIgnitionOn", "Latitude", "Load", "Longitude", "NumSatellites", "Odometer", "Reason", "ReasonString", "Reference", "Region", "SiteName", "SpeedOverGround", "Status", "Street", "TrackTrue", "TripDestination", "TripID", "TripSource", "TripType", "UTCDateTime", "WaybillNumber")
Code
# data cleaning

df %<>% 
  #dplyr::select(tidyselect::all_of(var)) %>% 
  mutate(DateTimeLocal = str_remove_all(`DateTimeLocal`, 
                                        paste(remove, collapse = "|")),
         DateTimeLocal = str_remove_all(`DateTimeLocal`, 
                                        "\\+0100|\\+0000"),
         DateTimeLocal = as.numeric(`DateTimeLocal`),
         DateTimeLocal = `DateTimeLocal`/1000,
         DateTimeLocal = anytime(`DateTimeLocal`),
         DateTimeReceived = str_remove_all(DateTimeReceived, 
                                           paste(remove, collapse = "|")),
         DateTimeReceived = str_remove_all(DateTimeReceived, 
                                           "\\+0100|\\+0000"),
         DateTimeReceived = as.numeric(DateTimeReceived),
         DateTimeReceived = DateTimeReceived/1000,
         DateTimeReceived = anytime(DateTimeReceived),
         DepartureDateTime = str_remove_all(DepartureDateTime, 
                                            paste(remove, collapse = "|")),
         DepartureDateTime = str_remove_all(DepartureDateTime, 
                                            "\\+0100|\\+0000"),
         DepartureDateTime = as.numeric(DepartureDateTime),
         DepartureDateTime = DepartureDateTime/1000,
         DepartureDateTime = anytime(DepartureDateTime),
         UTCDateTime = str_remove_all(UTCDateTime, 
                                      paste(remove, collapse = "|")),
         UTCDateTime = str_remove_all(UTCDateTime, 
                                      "\\+0100|\\+0000"),
         UTCDateTime = as.numeric(UTCDateTime),
         UTCDateTime = UTCDateTime/1000,
         UTCDateTime = anytime(UTCDateTime),
         LastIgnitionOff = str_remove_all(LastIgnitionOff, 
                                          paste(remove, collapse = "|")),
         LastIgnitionOff = str_remove_all(LastIgnitionOff, 
                                          "\\+0100|\\+0000"),
         LastIgnitionOff = as.numeric(LastIgnitionOff),
         LastIgnitionOff = LastIgnitionOff/1000,
         LastIgnitionOff = anytime(LastIgnitionOff),
         LastIgnitionOn = str_remove_all(LastIgnitionOn, 
                                         paste(remove, collapse = "|")),
         LastIgnitionOn = str_remove_all(LastIgnitionOn, 
                                         "\\+0100|\\+0000"),
         LastIgnitionOn = as.numeric(LastIgnitionOn),
         LastIgnitionOn = LastIgnitionOn/1000,
         LastIgnitionOn = anytime(LastIgnitionOn)) %>% 
  filter(!between(TripID, 9000000000, Inf)) %>% 
  filter(!TripID == 0)

df %<>% mutate(TripID = as.numeric(TripID))
Code
options(scipen = 999)

# destination dataframe

ibese_df <- data.frame(id = "ibese", lon = c(3.043568), lat = c(7.006293))


ibese <- matrix(c(3.043568,7.006293), ncol = 2)

ibese_geofence <- c("Ibese", "IBESE", "Vehicle Park", "Vehicle Park 2")

Determining the direction of the truck

Code
inbound_df <- df %>% 
  filter(AssetStatus == "InService", Longitude > 0, 
         Latitude > 0) %>% 
  filter(!between(TripID, 9000000000, Inf)) %>% 
  filter(TripID != 0) %>% 
  select(Reference, DateTimeReceived, 
         Longitude, Latitude, TripID, Geofence, Altitude) %>% 
  arrange(Reference, DateTimeReceived) %>% 
  group_by(Reference, TripID) %>% 
  mutate(.after = DateTimeReceived,
         TimeDiff = as.numeric(difftime(DateTimeReceived, 
                                        lag(DateTimeReceived, 
                                            default = first(DateTimeReceived)), 
                                        units = "hours")),
         LongLat = matrix(c(Longitude, Latitude), ncol = 2),
         DistCovered = distGeo(LongLat, lag(LongLat)),
         DistCovered = DistCovered/1000,
         DistToPlant = distGeo(LongLat, ibese),
         DistToPlant = DistToPlant/1000) %>%
  mutate(Direction = if_else(lag(DistToPlant) < DistToPlant, "Inbound", "Outbound")) %>% 
  filter(Direction == "Inbound") %>% 
  slice_tail(n = 1) %>% 
  filter(TripID != 3000071349)

Estimating the arrival time and distance

Code
duration <- osrmTable(src = df %>%
                        # filter(as.Date(DateTimeReceived) == today()) %>% 
                        select(TripID, Longitude, Latitude) %>% 
                        slice_head(n = 100),
                      dst = ibese_df, measure = "duration", osrm.profile = "car")


distance <- osrmTable(src = df %>%
                        # filter(as.Date(DateTimeReceived) == today()) %>% 
                        select(TripID, Longitude, Latitude) %>% 
                        slice_head(n = 100),
                      dst = ibese_df, measure = "distance", osrm.profile = "car")


distances <- distance$distances %>% 
  as.data.frame() %>%
  rownames_to_column() %>% 
  rename(TripID = rowname,
         Distance = ibese) %>% 
  mutate(TripID = as.numeric(TripID))


durations <- duration$durations %>% 
  as.data.frame() %>%
  rownames_to_column() %>% 
  rename(TripID = rowname,
         duration = ibese) %>% 
  mutate(TripID = as.numeric(TripID))

Data Join

Joining the durations, distances and inbound_df data frames.

Code
arrival <- df %>%
  inner_join(durations, by = 'TripID') %>% 
  inner_join(distances, by = 'TripID') %>% 
  mutate(Arrival = DateTimeReceived + minutes(round(duration))) %>% 
  select(TripID, Reference, Longitude, Latitude, Distance, DateTimeReceived, Arrival) %>% 
  arrange(Distance)

#DT::datatable(arrival)