Get started

openrouteservice R package provides easy access to the openrouteservice (ORS) API from R. It allows you to painlessly consume the following services:

Disclaimer

By using this package, you agree to the ORS terms and conditions.

Installation

The package is not yet available from CRAN, but you can install the development version directly from GitHub.

Setting up API key

In order to start using ORS services you first need to set up your personal API key, which you can get for free.

library(openrouteservice)

ors_api_key("<your-api-key>")

This will save the key in the default keyring of your system credential store. Once the key is defined, it persists in the keyring store of the operating system. This means that it survives beyond the termination of the R session, so you don’t need to set it again each time you start a new R session. To retrieve the key just call ors_api_key() without the key argument.

Alternatively, they key can be provided in the environment variable ORS_API_KEY. The value from the environment variable takes precedence over the former approach allowing to bypass the keyring infrastructure.

Directions

ors_directions() interfaces the ORS directions service to compute routes between given coordinates.

library(openrouteservice)

coordinates <- list(c(8.34234, 48.23424), c(8.34423, 48.26424))

x <- ors_directions(coordinates)

Way points can be provided as a list of coordinate pairs c(lon, lat), or a 2-column matrix-like object such as a data frame.

coordinates <- data.frame(lon = c(8.34234, 8.34423), lat = c(48.23424, 48.26424))

The response formatting defaults to geoJSON which allows to easily visualize it with e.g. leaflet.

library(leaflet)

leaflet() %>%
  addTiles() %>%
  addGeoJSON(x, fill=FALSE) %>%
  fitBBox(x$bbox)

Other output formats, such as GPX, can be specified in the argument format. Note that plain JSON response returns the geometry as Google’s encoded polyline,

x <- ors_directions(coordinates, format = "json")

geometry <- x$routes[[1]]$geometry
str(geometry)
##  chr "mtkeHuv|q@~@VLHz@\\PR|@hBt@j@^n@L\\NjALv@Jh@NXi@zBm@jCKTy@z@qAhBa@\\[Ne@DgCc@i@?[Ty@hAi@zASRi@R}@H_@N[b@kAdCy@`"| __truncated__

so an additional postprocessing step might be necessary.

library(googlePolylines)
str(decode(geometry))
## List of 1
##  $ :'data.frame':    165 obs. of  2 variables:
##   ..$ lat: num [1:165] 48.2 48.2 48.2 48.2 48.2 ...
##   ..$ lon: num [1:165] 8.34 8.34 8.34 8.34 8.34 ...

The API offers a wide range of profiles for multiple modes of transport, such as: car, heavy vehicle, different bicycle types, walking, hiking and wheelchair. These can be listed with

##                car                hgv               bike           roadbike       mountainbike 
##      "driving-car"      "driving-hgv"  "cycling-regular"     "cycling-road" "cycling-mountain" 
##             e-bike            walking             hiking         wheelchair 
## "cycling-electric"     "foot-walking"      "foot-hiking"       "wheelchair"

Each of these modes uses a carefully compiled street network to suite the profiles requirements.

x <- ors_directions(coordinates, profile="cycling-mountain")

leaflet() %>%
  addTiles() %>%
  addGeoJSON(x, fill=FALSE) %>%
  fitBBox(x$bbox)

Any optional query parameters can be specified by providing them as additional ... arguments to ors_directions. For example, in order to plot the elevation profile of a route colored by steepness use elevation = TRUE to add height to the coordinates of the points along the route and query for steepness in extra_info.

library("sf")

x <- ors_directions(coordinates, profile = "cycling-mountain", elevation = TRUE,
                    extra_info = I("steepness"), output = "sf")

height <- st_geometry(x)[[1]][, 3]

Here we use simple features output for the sake of easy postprocessing which includes finding the length of individual route segments and their distance relative to the starting point. These can be computed with st_distance() upon converting the LINESTRING to a list of POINTs,

points <- st_cast(st_geometry(x), "POINT")
n <- length(points)
segments <- cumsum(st_distance(points[-n], points[-1], by_element = TRUE))

while their steepness can be extracted from the requested metadata.

steepness <- x$extras$steepness$values
steepness <- rep(steepness[,3], steepness[,2]-steepness[,1])
steepness <- factor(steepness, -5:5)

palette = setNames(rev(RColorBrewer::brewer.pal(11, "RdYlBu")), levels(steepness))

For the final plot we use ggplot2 in combinations with ggforce which supports handling of length units associated with the data.

library("ggplot2")
library("ggforce")
library("units")

units(height) <- as_units("m")

df <- data.frame(x1 = c(set_units(0, "m"), segments[-(n-1)]),
                 x2 = segments,
                 y1 = height[-n],
                 y2 = height[-1],
                 steepness)

y_ran = range(height) * c(0.9, 1.1)

n = n-1

df2 = data.frame(x = c(df$x1, df$x2, df$x2, df$x1),
                 y = c(rep(y_ran[1], 2*n), df$y2, df$y1),
                 steepness,
                 id = 1:n)

ggplot() + theme_bw() +
  geom_segment(data = df, aes(x1, y1, xend = x2, yend = y2), size = 1) +
  geom_polygon(data = df2, aes(x, y, group = id), fill = "white") +
  geom_polygon(data = df2, aes(x, y , group = id, fill = steepness)) +
  scale_fill_manual(values = alpha(palette, 0.8), drop = FALSE) +
  scale_x_unit(unit = "km", expand = c(0,0)) +
  scale_y_unit(expand = c(0,0), limits = y_ran) +
  labs(x = "Distance", y = "Height")

Advanced options are natively formatted as JSON objects, but can be passed as their R list representation.

polygon = list(
    type = "Polygon",
    coordinates = list(
      list(
        c(8.330469, 48.261570),
        c(8.339052, 48.261570),
        c(8.339052, 48.258227),
        c(8.330469, 48.258227),
        c(8.330469, 48.261570)
      )
    ),
    properties = ""
  )

options <- list(
  avoid_polygons = polygon
)

x <- ors_directions(coordinates, profile="cycling-mountain", options=options)

leaflet() %>%
  addTiles() %>%
  addGeoJSON(polygon, color="#F00") %>%
  addGeoJSON(x, fill=FALSE) %>%
  fitBBox(x$bbox)

Isochrones

Reachability has become a crucial component for many businesses from all different kinds of domains. ors_isochrones() helps you to determine which areas can be reached from certain location(s) in a given time or travel distance. The reachability areas are returned as contours of polygons. Next to the range provided in seconds or meters you may as well specify the corresponding intervals. The list of optional arguments to ors_isochrones() is similar as to ors_directions().

library(mapview)

coordinates <- data.frame(lon = c(8.34234, 8.34234), lat = c(48.23424, 49.23424))

## 1 hour range split into 20 minute intervals
res <- ors_isochrones(coordinates, range = 3600, interval = 1200, output = "sf")
res
## Simple feature collection with 6 features and 3 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 7.650154 ymin: 47.73843 xmax: 9.175435 ymax: 50.00795
## epsg (SRID):    4326
## proj4string:    +proj=longlat +datum=WGS84 +no_defs
##   group_index value              center                       geometry
## 1           0  1200 8.344268, 48.233826 POLYGON ((8.189468 48.23579...
## 2           0  2400 8.344268, 48.233826 POLYGON ((8.028887 48.14995...
## 3           0  3600 8.344268, 48.233826 POLYGON ((7.834111 48.05418...
## 4           1  1200 8.343705, 49.234211 POLYGON ((8.130639 49.26213...
## 5           1  2400 8.343705, 49.234211 POLYGON ((7.927629 49.19169...
## 6           1  3600 8.343705, 49.234211 POLYGON ((7.650154 49.44253...
values <- levels(factor(res$value))
ranges <- split(res, values)
ranges <- ranges[rev(values)]

names(ranges) <- sprintf("%s min", as.numeric(names(ranges))/60)

mapview(ranges, alpha.regions = 0.2, homebutton = FALSE, legend = FALSE)

Here we have used sf output for the sake of some further postprocessing and visualization. By grouping the isochrones according to ranges we gain the ability of toggling individual ranges when displayed in mapview. Another option could be to group by locations. The following example illustrates a possible approach to applying a custom color palette to the non-overlapping parts of isochrones.

locations = split(res, res$group_index)

locations <- lapply(locations, function(loc) {
  g <- st_geometry(loc)
  g[-which.min(values)] <- st_sfc(Map(st_difference,
                                      g[match(values[-which.min(values)], loc$value)],
                                      g[match(values[-which.max(values)], loc$value)]))
  st_geometry(loc) <- g
  loc
})

isochrones <- unsplit(locations, res$group_index)

pal <- setNames(heat.colors(length(values)), values)
mapview(isochrones, zcol = "value", col = pal, col.regions = pal,
        alpha.regions = 0.5, homebutton = FALSE)

Matrix

One to many, many to many or many to one: ors_matrix() allows you to obtain aggregated time and distance information between a set of locations (origins and destinations). Unlike ors_directions() it does not return detailed route information. But you may still specify the transportation mode and compute routes which adhere to certain restrictions, such as avoiding specific road types or object characteristics.

coordinates <- list(
  c(9.970093, 48.477473),
  c(9.207916, 49.153868),
  c(37.573242, 55.801281),
  c(115.663757,38.106467)
)

# query for duration and distance in km
res <- ors_matrix(coordinates, metrics = c("duration", "distance"), units = "km")

# duration in hours
(res$durations / 3600) %>% round(1)
##       [,1]  [,2] [,3]  [,4]
## [1,]   0.0   1.7 25.3 111.3
## [2,]   1.7   0.0 25.0 111.0
## [3,]  25.0  24.7  0.0  87.3
## [4,] 111.3 111.0 87.1   0.0
##       [,1]  [,2] [,3]  [,4]
## [1,]     0   155 2395 10213
## [2,]   156     0 2377 10195
## [3,]  2352  2325    0  7633
## [4,] 10177 10150 7642     0

Geocoding

ors_geocode() transforms a description of a location provided in query, such as the place’s name, street address or postal code, into a normalized description of the location with a point geometry. Additionally, it offers reverse geocoding which does exactly the opposite: It returns the next enclosing object which surrounds the coordinates of the given location. To obtain more relevant results you may also set a radius of tolerance around the requested coordinates.

## set the number of results returned
x <- ors_geocode("Heidelberg", size = 1)

## search within a particular country
x <- ors_geocode("Heidelberg", boundary.country = "DE")

## structured geocoding
x <- ors_geocode(list(locality="Heidelberg", county="Heidelberg"))

## reverse geocoding
location <- x$features[[1L]]$geometry$coordinates

y <- ors_geocode(location = location, layers = "locality", size = 1)

POIs

This service allows you to find places of interest around or within given geographic coordinates. You may search for given features around a point, path or even within a polygon specified in geometry. To list all the available POI categories use ors_pois('list').

## Simple feature collection with 1 feature and 5 fields
## geometry type:  POINT
## dimension:      XY
## bbox:           xmin: 8.80294 ymin: 53.0757 xmax: 8.80294 ymax: 53.0757
## epsg (SRID):    4326
## proj4string:    +proj=longlat +datum=WGS84 +no_defs
##   category_ids osm_tags distance osm_type    osm_id                geometry
## 1 kiosk, shops      yes 32.95127        1 726580652 POINT (8.80294 53.0757)

You can gather statistics on the amount of certain POIs in an area by using request='stats'.

## <ors_pois>
## List of 2
##  $ places     :List of 2
##   ..$ total_count: int 5
##   ..$ shops      :List of 3
##   .. ..$ group_id   : int 420
##   .. ..$ categories :List of 1
##   .. .. ..$ kiosk:List of 2
##   .. .. .. ..$ count      : int 5
##   .. .. .. ..$ category_id: int 488
##   .. ..$ total_count: int 5
##  $ information:List of 4
##   ..$ attribution: chr "openrouteservice.org | OpenStreetMap contributors"
##   ..$ version    : chr "0.1"
##   ..$ timestamp  : int 1558616856
##   ..$ query      :List of 5
##   .. ..$ request : chr "stats"
##   .. ..$ limit   : int 2000
##   .. ..$ sortby  : chr "distance"
##   .. ..$ filters :List of 1
##   .. .. ..$ category_ids: int 488
##   .. ..$ geometry:List of 2
##   .. .. ..$ geojson:List of 2
##   .. .. .. ..$ type       : chr "Point"
##   .. .. .. ..$ coordinates: num [1:2] 8.8 53.1
##   .. .. ..$ buffer : int 500

Elevation

Given a point or line geometry you can use ors_elevation to query for its elevation.

x <- ors_geocode("Königstuhl", output = "sf")

ors_elevation("point", st_coordinates(x))
## <ors_elevation>
##  num [1:3] 8.72 49.4 560