ropensci / stplanr

Sustainable transport planning with R
https://docs.ropensci.org/stplanr
Other
417 stars 66 forks source link

stplanr::overline() producing overlapping route sections with gh_get_route() #478

Closed joshpersi closed 2 years ago

joshpersi commented 2 years ago

Hi everyone,

I'm having some trouble getting stplanr's overline() to work with the graphhopper api from the graphhopper-r package. The issue seems to be that when overline() is applied to routes calculated with graphhopper::gh_get_route(), overline produces some segments that do not overlap when using the OSRM api and stplanr::get_route().

In the reprex below, I am mapping three routes from Toronto to three other cities. On the leaflet map, particularly the section between Oshawa and Bowmanville, you'll see two main segments that are a darker colour than the rest.

If you zoom in all the way on these darker sections and mouse-over perpendicularly to the section, you may see the number of trips change from 10 to 5. For some reason, graphhopper seems to be separating the route at these segments and a) I don't understand the reason and b) I don't know how to combine the 10-trip and 5-trip segments. Any help you can provide would be much appreciated.

# Packages ----------------------------------------------------------------
library(graphhopper)
library(tidyverse)
library(stplanr)
library(osrm)
library(sf)
library(leaflet)

# Set graphhopper API -----------------------------------------------------
Sys.setenv(GH_API_KEY = "add-your-API-here")
gh_set_api_url("https://graphhopper.com/api/1/")

# Data --------------------------------------------------------------------
my_data <- data.table::data.table(
  origin_id = rep("Toronto", 3),
  origin_lat = rep(43.6516053, 3),
  origin_lon = rep(-79.3831254, 3),
  dest_id = c("Oshawa", "Bowmanville", "Port Hope"),
  dest_lat = c(43.8953355, 43.9123613, 43.960443),
  dest_lon = c(-78.8659502, -78.6892641, -78.291539),
  num_trips = c(15L, 10L, 5L)
  )

# Route mapping with graphhopper ------------------------------------------
# Map routes with graphhopper and convert to a sf object
my_routes_gh <- my_data %>%
  rowwise() %>%
  mutate(
    route = list(gh_get_route(
      list(
        c(origin_lat, origin_lon),
        c(dest_lat, dest_lon)
      )
    ))
  ) %>%
  mutate(route = list(gh_as_sf(route))) %>%
  unnest(col = route) %>%
  st_as_sf() 

# Estimate transport flow with stplanr::overline
my_overline_gh <- overline(my_routes_gh, attrib = "num_trips", regionalise = 1e8)

# Create a color palette for the number of trips made on a section of the route
pal_gh <- colorNumeric(
  palette = "plasma",
  domain = my_overline_gh$num_trips
)

# Create labels for the number of trips made on a section of the route
route_labels_gh <- paste("<B>Number of trips: </B>", my_overline_gh$num_trips)
location_labels_gh <- paste("<B>City name: </B>", my_data$dest_id)

# Plot the transport flow data in leaflet. Notice that some route sections are
# overlapping and if you a) zoom in all the way on an overlapping section and b)
# mouse over the overlap, you may be able to see the labels change
leaflet() %>%
  addProviderTiles("CartoDB.PositronNoLabels") %>% 
  addCircleMarkers(
    data = my_data, 
    lat = ~dest_lat,
    lng = ~dest_lon,
    label = lapply(location_labels_gh, htmltools::HTML),
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "15px",
      direction = "auto"
    )
  ) %>% 
  addPolylines(
    data = my_overline_gh,
    color = ~ pal_gh(num_trips),
    highlightOptions = highlightOptions(
      weight = 10,
      color = "#666",
      bringToFront = TRUE
    ),
    label = lapply(route_labels_gh, htmltools::HTML),
    labelOptions = labelOptions(
      style = list("font-weight" = "normal", padding = "3px 8px"),
      textsize = "15px",
      direction = "auto"
    )
  ) %>% 
  addLegend(data = my_overline_gh, pal = pal_gh, values = ~num_trips)
Robinlovelace commented 2 years ago

Hi @josh-persi can you show the output your seeing and describe the output you would expect? I tried your reprex but hit errors on the bit beginning my_data <-... I tried to test out the graphhopper package and could not get the minimal reproducible example working, can you get this working? I suggest generating some routes, uploading them as a .GeoJSON file somewhere, and starting with those, e.g. with

input_routes = sf::read_sf("https://github.com/npct/pct-outputs-regional-notR/raw/master/commute/msoa/isle-of-wight/rf.geojson")

Here's what I get in any case:

library(graphhopper)
Sys.setenv(GH_API_KEY = "key")
gh_set_api_url("https://graphhopper.com/api/1/")
# ?graphhopper::gh_as_sf
# test it
start_point <- c(52.592204, 13.414307)
end_point <- c(52.539614, 13.364868)

route_sf <- gh_get_route(list(start_point, end_point)) %>%
  gh_as_sf()
#> No encoding supplied: defaulting to UTF-8.
#> Error in decode.default(path$points): I don't know how to decode this object

Happy to reopen this if the issue can be reproduced. I suggest checking out the reprex package: copy the code and then run reprex::reprex() to get reproducible example + output.

Created on 2021-12-23 by the reprex package (v2.0.1)

joshpersi commented 2 years ago

Hi @Robinlovelace,

Many thanks for your speedy response.

I tried to create a reprex but there seems to be an issue related to my Graphhopper map. In it's place, I've uploaded a R Markdown document that shows the issue along with .geojson files containing the routes produced by graphhopper::gh_get_route() and by get_routes() with the OSRM API. Both of these can be accessed on this Google Drive.

In the R markdown file, I plot routes three routes calculated with graphhopper and OSRM. Both routing functions produce similar and expected data (one linestring per route) but the differences arise when passing these routes through overline(). With the OSRM routes, overline() produces the expected result; a row for each distinct number of trips and route geometry. With the graphhopper routes, overline() produces many more routes, including split route segments that are identifiable in the leaflet map.

I am interested in using graphhopper because it allows for routing with transport truck profiles. If you happen to know of another API with this capability, I'd be happy to hear it.

Please let me know if you need any other information.