Closed e-kotov closed 3 years ago
Hi, thanks for reporting! I am happy about contributions and also testing if you have time.
Currently the HERE Routing API v7 is used in the package, the link to the documentation is for v8.12.0. I am planning to update to the HERE Routing API v8 in the next weeks. The API documentation for version 7 is here, but the handling of timestamps seems to be similar:
Time when travel is expected to start. Traffic speed and incidents are taken into account when calculating the route, where applicable. You can use now
to specify the current time. It can be used only if parameter start
is also used. Type: xs:dateTime
.
departure=2013-07-04T17:00:00+02
When the optional timezone offset is not specified time is assumed to be local.
If I understand your issue correctly, then the returned datetime is in the correct timezone, but the traffic information is shifted in time. Which means a timestamp with a specified optional timezone offset should be passed in the requests to the API to avoid conversion to local time by the API. A rewrite of the .encode_datetime
to add the timezone offset:
.encode_datetime <- function(datetime) {
dt <- format(datetime, "%Y-%m-%dT%H:%M:%S%z")
stringr::str_replace(
paste0(
stringr::str_sub(dt, 1, -3), ":",
stringr::str_sub(dt, -2, nchar(dt))
),
"\\+", "%2B"
)
}
# Plus (encoded)
(t = as.POSIXct("2020-09-17 18:00:00", tz = "Europe/Moscow"))
#> [1] "2020-09-17 18:00:00 MSK"
.encode_datetime(t)
#> [1] "2020-09-17T18:00:00%2B03:00"
# Minus
(t = as.POSIXct("2020-09-17 18:00:00", tz = "America/Bogota"))
#> [1] "2020-09-17 18:00:00 -05"
.encode_datetime(t)
#> [1] "2020-09-17T18:00:00-05:00"
The example gives now more reliable results:
library(hereR)
set_verbose(TRUE)
library(sf)
#> Linking to GEOS 3.8.1, GDAL 3.1.1, PROJ 6.3.1
point <- st_sf(st_sfc(st_point(c(37.52523, 55.64005)), crs = 4326))
area_18 <- isoline(poi = point,
datetime = as.POSIXct("2020-09-17 18:00:00", tz = "Europe/Moscow"),
arrival = F,
range = 1800,
range_type = "time",
type = "fastest",
mode = "car",
traffic = T,
aggregate = F) %>% st_area()
#> Sending 1 request(s) to: 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?...'
#> Received 1 response(s) with total size: 18.3 Kb
area_21 <- isoline(poi = point,
datetime = as.POSIXct("2020-09-17 21:00:00", tz = "Europe/Moscow"),
arrival = F,
range = 1800,
range_type = "time",
type = "fastest",
mode = "car",
traffic = T,
aggregate = F) %>% st_area()
#> Sending 1 request(s) to: 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?...'
#> Received 1 response(s) with total size: 27.6 Kb
area_no_traffic <- isoline(poi = point,
arrival = F,
range = 1800,
range_type = "time",
type = "fastest",
mode = "car",
traffic = F,
aggregate = F) %>% st_area()
#> Sending 1 request(s) to: 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?...'
#> Received 1 response(s) with total size: 15.6 Kb
area_18/area_21
#> 0.592572 [1]
area_21/area_no_traffic
#> 0.6583742 [1]
area_18/area_no_traffic
#> 0.3901341 [1]
The timezone of datetime values returned by route()
, route_matrix()
, … should be parsed back to the timezone of the original input datetimes in the request:
datetime_MSK <- as.POSIXct("2020-09-17 18:00:00", tz = "Europe/Moscow")
datetime_CEST <- as.POSIXct("2020-09-17 18:00:00", tz = "Europe/Zurich")
area_18_MSK <- isoline(poi = point,
datetime = datetime_MSK,
arrival = F,
range = 1800,
range_type = "time",
type = "fastest",
mode = "car",
traffic = T,
aggregate = F)
#> Sending 1 request(s) to: 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?...'
#> Received 1 response(s) with total size: 18.3 Kb
area_18_CEST <- isoline(poi = point,
datetime = datetime_CEST,
arrival = F,
range = 1800,
range_type = "time",
type = "fastest",
mode = "car",
traffic = T,
aggregate = F)
#> Sending 1 request(s) to: 'https://isoline.route.ls.hereapi.com/routing/7.2/calculateisoline.json?...'
#> Received 1 response(s) with total size: 18.1 Kb
data.frame(
request = c(1,2),
requested_time = c(
datetime_MSK %>% format("%Y-%m-%dT%H:%M:%S%z"),
datetime_CEST %>% format("%Y-%m-%dT%H:%M:%S%z")),
parsed_response = c(
area_18_MSK$departure %>% format("%Y-%m-%dT%H:%M:%S%z"),
area_18_CEST$departure %>% format("%Y-%m-%dT%H:%M:%S%z")),
area = c(area_18_MSK %>% st_area(), area_18_CEST %>% st_area())
)
#> request requested_time parsed_response area
#> 1 1 2020-09-17T18:00:00+0300 2020-09-17T18:00:00+0300 377560536 [m^2]
#> 2 2 2020-09-17T18:00:00+0200 2020-09-17T18:00:00+0200 455100512 [m^2]
Can you test again with the development version?
@munterfinger hello again! Thank you for such a quick response! I have tested the develop
branch - it seems to be working as expected now.
Perfect - thanks for the detailed description and investigation of the timezone issue.
A new release of the package with this important bugfix will be submitted to CRAN by the end of this week.
Hi, I have done some testing and some reading and it seems to me that the current approach with
.encode_datetime
is doing more harm than good. According to the Here API documentation (perhaps this is a new behaviour... and previously it worked in UTC???) :departureTime
specifies the time of departure as defined by eitherdate-time
orfull-date
T
partial-time
inRFC 3339
, section 5.6 (for example,2019-06-24T01:23:45
). The requested time is converted to the local time at origin. When the optional timezone offset is not specified, time is assumed to be local. If neither departureTime or arrivalTime are specified, current time at departure location will be used. All Time values in the response are returned in the timezone of each location.So when the iso and routing functions convert the time to UTC for sending to the Here REST API, they are simply passing something like
2020-09-17T15:00:00
. Here sees noZ
and nooptional timezone offset
. It then thinks that this is local time. So it returns the iso or the route for the incorrect time.To check that, try your own API key and run:
You will get:
This basically means that an isochrone for 18:00 in Moscow during the heaviest traffic time is 25% larger than at 21:00, which is highly unlikely. I think that supports my argument converting the time to UTC is undesirable and currently leads to incorrect results for all functions (isochrones, routing...).
If you agree with me, I can find some time in the next week or so to try and update the code as necessary and submit a pull request. The two possible solutions, I guess, are:
.encode_datetime
altogether.encode_datetime
to format the date for the URL with the offset according to theRFC 3339
, that is instead of converting"2020-09-17 18:00:00", tz = "Europe/Moscow"
to"2020-09-17T15:00:00"
(so now it is in UTC), convert it to"2020-09-17T15:00:00-03:00"
, so that Here knows that we need the offset time (I guess this will yield the same results as the first option, but is probably more reliable in the long run).