Skip to content

Commit db141b6

Browse files
authored
Add files via upload
1 parent d754633 commit db141b6

File tree

2 files changed

+126
-31
lines changed

2 files changed

+126
-31
lines changed

README.Rmd

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "cppRouting package"
2+
title: "cppRouting package v1.2"
33
author: "Vincent LARMET"
44
date: "July 6, 2019"
55
output: github_document
@@ -57,14 +57,19 @@ Data has to be a 3 columns data.frame or matrix containing from, to and a cost/d
5757
- `get_multi_paths` : compute shortest paths between all origin nodes and all destination nodes (*one-to-many*),
5858
- `get_isochrone` : compute isochrones/isodistances with one or multiple breaks.
5959
- `cpp_simplify` : remove non-intersection nodes, duplicated edges and isolated loops in the graph. Graph topology is preserved so distance calculation is faster and remains true. This function can be applied to very large graphs (several millions of nodes).
60+
- `get_detour` : return nodes that are reachable within a fixed additional cost around shortest paths. This function can be useful in producing accessibility indicators.
6061

6162

6263
###Path algorithms
6364
The choice between all the algorithms is available for *one-to-one* calculation like `get_distance_pair` and `get_path_pair`.
6465
In these functions, uni-directional Dijkstra algorithm is stopped when the destination node is reached.
6566
`A*` and `NBA*` are relevant if geographic coordinates of all nodes are provided. Note that coordinates should be expressed in a **projection system**.
6667
To be accurate and efficient, `A*` and `NBA*` algorithms should use an admissible heuristic function (here the Euclidean distance), e.g cost and heuristic function must be expressed in the same unit.
67-
In `cppRouting`, heuristic function `h` is defined such that : h(xi,yi,xdestination,ydestination)/k, with a constant k; so in the case where coordinates are expressed in meters and cost is expressed in time, k is the maximum speed allowed on the road. By default, constant is 1 and is designed for graphs with cost expressed in the same unit than coordinates (for example in meters).
68+
In `cppRouting`, heuristic function `h` for a node (n) is defined such that :
69+
**h(n,d) = ED(n,d) / k**
70+
with *h* the heuristic, *ED* the Euclidean distance, *d* the destination node and a constant *k*.
71+
72+
So in the case where coordinates are expressed in meters and cost is expressed in time, *k* is the maximum speed allowed on the road. By default, constant is 1 and is designed for graphs with cost expressed in the same unit than coordinates (for example in meters).
6873

6974
If coordinates cannot be provided, bi-directional Dijkstra algorithm can offer a good alternative to A* in terms of performance.
7075

@@ -186,12 +191,56 @@ p<-ggmap(dijon)+
186191
p
187192
188193
```
194+
195+
196+
###Compute possible detours within a fixed additional cost
197+
`get_detour` function returns all reachable nodes within a fixed detour time around the shortest path between origin and destination nodes. Returned nodes (n) meet the following condition :
198+
**SP(o,n) + SP(n,d) < SP(o,d) + t**
199+
with *SP* shortest distance/time, *o* the origin node, *d* the destination node and *t* the extra cost.
200+
The algorithm used is a slightly modified bidirectional Dijkstra.
201+
Let's see an example for the path between Dijon and Lyon city :
202+
```{r,echo=TRUE,message=FALSE,warning=FALSE}
203+
#Compute shortest path
204+
trajet<-get_path_pair(graph,from="205793",to="212490")
205+
206+
#Compute shortest path
207+
distance<-get_distance_pair(graph,from="205793",to="212490")
208+
209+
#Compute detour time of 25 and 45 minutes
210+
det25<-get_detour(graph,from="205793",to="212490",extra=25)
211+
det45<-get_detour(graph,from="205793",to="212490",extra=45)
212+
213+
#Create sf object of nodes
214+
pts<-st_as_sf(coord,coords=c("X","Y"),crs=2154)
215+
pts<-st_transform(pts,crs=4326)
216+
pts$time<-ifelse(pts$ID %in% unlist(det45),"45","0")
217+
pts$time<-ifelse(pts$ID %in% unlist(det25),"25",pts$time)
218+
pts$time<-ifelse(pts$ID %in% unlist(trajet),"Shortest Path",pts$time)
219+
pts$time<-factor(pts$time,levels = c("25","45","Shortest Path","0"))
220+
221+
#Plot
222+
dijon=get_map(location=c(lon=5.041140,lat=46.48),zoom=8, source="google",maptype = "toner-2010")
223+
224+
p<-ggmap(dijon)+
225+
geom_sf(data=pts[pts$time!="0",],aes(color=time),inherit.aes = FALSE)+
226+
ggtitle(paste0("Detours around Dijon-lyon path - ",round(distance,digits = 2)," minutes"))+
227+
labs(color="Minutes")+
228+
theme(axis.text.x = element_blank(),
229+
axis.text.y = element_blank(),
230+
axis.ticks = element_blank(),
231+
axis.title.y=element_blank(),axis.title.x=element_blank())
232+
p
233+
234+
```
235+
236+
189237

190238
###Graph simplification
191239
`cpp_simplify`'s internal function performs two major steps :
192240

193241
- removing non-intersection nodes between two intersection nodes then calculate cost of the new edges,
194242
- removing duplicate edges that are potentially created in the first step.
243+
195244
In order to remove maximum number of nodes, some iterations are needed until only intersection nodes are remained.
196245

197246
Let's see a small example :
@@ -463,5 +512,4 @@ setdiff(test_dodgr[[1]][[1]],test_cpp[[1]])
463512

464513
#New algorithms `cppRouting` will provide in the future
465514

466-
- Detours admitting shortest paths : finding the nodes that are reachable under a fixed detour time around the shortest path
467515
- Contraction hierarchies implementation

README.md

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cppRouting package
1+
cppRouting package v1.2
22
================
33
Vincent LARMET
44
July 6, 2019
@@ -59,14 +59,18 @@ Main functions
5959
- `get_multi_paths` : compute shortest paths between all origin nodes and all destination nodes (*one-to-many*),
6060
- `get_isochrone` : compute isochrones/isodistances with one or multiple breaks.
6161
- `cpp_simplify` : remove non-intersection nodes, duplicated edges and isolated loops in the graph. Graph topology is preserved so distance calculation is faster and remains true. This function can be applied to very large graphs (several millions of nodes).
62+
- `get_detour` : return nodes that are reachable within a fixed additional cost around shortest paths. This function can be useful in producing accessibility indicators.
6263

6364
### Path algorithms
6465

6566
The choice between all the algorithms is available for *one-to-one* calculation like `get_distance_pair` and `get_path_pair`.
6667
In these functions, uni-directional Dijkstra algorithm is stopped when the destination node is reached.
6768
`A*` and `NBA*` are relevant if geographic coordinates of all nodes are provided. Note that coordinates should be expressed in a **projection system**.
6869
To be accurate and efficient, `A*` and `NBA*` algorithms should use an admissible heuristic function (here the Euclidean distance), e.g cost and heuristic function must be expressed in the same unit.
69-
In `cppRouting`, heuristic function `h` is defined such that : h(xi,yi,xdestination,ydestination)/k, with a constant k; so in the case where coordinates are expressed in meters and cost is expressed in time, k is the maximum speed allowed on the road. By default, constant is 1 and is designed for graphs with cost expressed in the same unit than coordinates (for example in meters).
70+
In `cppRouting`, heuristic function `h` for a node (n) is defined such that :
71+
**h(n,d) = ED(n,d) / k** with *h* the heuristic, *ED* the Euclidean distance, *d* the destination node and a constant *k*.
72+
73+
So in the case where coordinates are expressed in meters and cost is expressed in time, *k* is the maximum speed allowed on the road. By default, constant is 1 and is designed for graphs with cost expressed in the same unit than coordinates (for example in meters).
7074

7175
If coordinates cannot be provided, bi-directional Dijkstra algorithm can offer a good alternative to A\* in terms of performance.
7276

@@ -148,7 +152,7 @@ pair_dijkstra<-get_distance_pair(graph,origin,destination)
148152
## Running Dijkstra ...
149153

150154
## user system elapsed
151-
## 58.47 0.77 59.52
155+
## 56.57 0.81 57.39
152156

153157
#### Using bi-directional Dijkstra algorithm
154158

@@ -162,7 +166,7 @@ pair_bidijkstra<-get_distance_pair(graph,origin,destination,algorithm = "bi")
162166
## Running bidirectional Dijkstra...
163167

164168
## user system elapsed
165-
## 37.04 1.42 38.50
169+
## 36.66 1.36 38.02
166170

167171
#### Using A\* algorithm
168172

@@ -178,7 +182,7 @@ pair_astar<-get_distance_pair(graph,origin,destination,algorithm = "A*",constant
178182
## Running A* ...
179183

180184
## user system elapsed
181-
## 31.64 1.95 33.65
185+
## 30.84 1.72 32.58
182186

183187
#### Using NBA\* algorithm
184188

@@ -192,21 +196,21 @@ pair_nba<-get_distance_pair(graph,origin,destination,algorithm = "NBA",constant
192196
## Running NBA* ...
193197

194198
## user system elapsed
195-
## 17.89 2.88 20.82
199+
## 17.77 2.70 20.47
196200

197201
#### Output
198202

199203
``` r
200204
head(cbind(pair_dijkstra,pair_bidijkstra,pair_astar,pair_nba))
201205
```
202206

203-
## pair_dijkstra pair_bidijkstra pair_astar pair_nba
204-
## [1,] 822.8825 822.8825 822.8825 822.8825
205-
## [2,] 255.7911 255.7911 255.7911 255.7911
206-
## [3,] 410.9381 410.9381 410.9381 410.9381
207-
## [4,] 613.2455 613.2455 613.2455 613.2455
208-
## [5,] 327.6106 327.6106 327.6106 327.6106
209-
## [6,] 357.9866 357.9866 357.9866 357.9866
207+
## pair_dijkstra pair_bidijkstra pair_astar pair_nba
208+
## [1,] 291.24281 291.24281 291.24281 291.24281
209+
## [2,] 306.65703 306.65703 306.65703 306.65703
210+
## [3,] 46.83659 46.83659 46.83659 46.83659
211+
## [4,] 387.52275 387.52275 387.52275 387.52275
212+
## [5,] 185.46024 185.46024 185.46024 185.46024
213+
## [6,] 103.39441 103.39441 103.39441 103.39441
210214

211215
##### In `get_distance_pair` function, all the algorithms can be ran in parallel by setting TRUE to allcores argument.
212216

@@ -248,13 +252,57 @@ p
248252

249253
![](README_files/figure-markdown_github/unnamed-chunk-11-1.png)
250254

255+
### Compute possible detours within a fixed additional cost
256+
257+
`get_detour` function returns all reachable nodes within a fixed detour time around the shortest path between origin and destination nodes. Returned nodes (n) meet the following condition :
258+
**SP(o,n) + SP(n,d) &lt; SP(o,d) + t**
259+
with *SP* shortest distance/time, *o* the origin node, *d* the destination node and *t* the extra cost.
260+
The algorithm used is a slightly modified bidirectional Dijkstra.
261+
Let's see an example for the path between Dijon and Lyon city :
262+
263+
``` r
264+
#Compute shortest path
265+
trajet<-get_path_pair(graph,from="205793",to="212490")
266+
267+
#Compute shortest path
268+
distance<-get_distance_pair(graph,from="205793",to="212490")
269+
270+
#Compute detour time of 25 and 45 minutes
271+
det25<-get_detour(graph,from="205793",to="212490",extra=25)
272+
det45<-get_detour(graph,from="205793",to="212490",extra=45)
273+
274+
#Create sf object of nodes
275+
pts<-st_as_sf(coord,coords=c("X","Y"),crs=2154)
276+
pts<-st_transform(pts,crs=4326)
277+
pts$time<-ifelse(pts$ID %in% unlist(det45),"45","0")
278+
pts$time<-ifelse(pts$ID %in% unlist(det25),"25",pts$time)
279+
pts$time<-ifelse(pts$ID %in% unlist(trajet),"Shortest Path",pts$time)
280+
pts$time<-factor(pts$time,levels = c("25","45","Shortest Path","0"))
281+
282+
#Plot
283+
dijon=get_map(location=c(lon=5.041140,lat=46.48),zoom=8, source="google",maptype = "toner-2010")
284+
285+
p<-ggmap(dijon)+
286+
geom_sf(data=pts[pts$time!="0",],aes(color=time),inherit.aes = FALSE)+
287+
ggtitle(paste0("Detours around Dijon-lyon path - ",round(distance,digits = 2)," minutes"))+
288+
labs(color="Minutes")+
289+
theme(axis.text.x = element_blank(),
290+
axis.text.y = element_blank(),
291+
axis.ticks = element_blank(),
292+
axis.title.y=element_blank(),axis.title.x=element_blank())
293+
p
294+
```
295+
296+
![](README_files/figure-markdown_github/unnamed-chunk-12-1.png)
297+
251298
### Graph simplification
252299

253300
`cpp_simplify`'s internal function performs two major steps :
254301

255302
- removing non-intersection nodes between two intersection nodes then calculate cost of the new edges,
256303
- removing duplicate edges that are potentially created in the first step.
257-
In order to remove maximum number of nodes, some iterations are needed until only intersection nodes are remained.
304+
305+
In order to remove maximum number of nodes, some iterations are needed until only intersection nodes are remained.
258306

259307
Let's see a small example :
260308

@@ -295,7 +343,7 @@ plot(igr2,edge.arrow.size=.3,edge.label=E(igr2)$dist,main="One iteration - keepi
295343
box(col="black")
296344
```
297345

298-
![](README_files/figure-markdown_github/unnamed-chunk-12-1.png)
346+
![](README_files/figure-markdown_github/unnamed-chunk-13-1.png)
299347

300348
Here, junction nodes are `e`, `h`, `d`, `k`, `l`, `i` and `m`. So `b`, `c`, `f` and `n` have been contracted in the first step of the function. By contracting `n`, an edge with cost of 2 has been created between `m` and `l` nodes.
301349
The second step of the function has removed this edge which is greater than the original one (e.g 1), and the whole process now need a second iteration to remove `m` and `l` that aren't intersection nodes anymore.
@@ -321,7 +369,7 @@ plot(igr4,edge.arrow.size=.3,edge.label=E(igr4)$dist,main="Second iteration - re
321369
box(col="black")
322370
```
323371

324-
![](README_files/figure-markdown_github/unnamed-chunk-13-1.png)
372+
![](README_files/figure-markdown_github/unnamed-chunk-14-1.png)
325373

326374
#### French road network simplification
327375

@@ -338,7 +386,7 @@ system.time(
338386
```
339387

340388
## user system elapsed
341-
## 14.77 0.97 15.79
389+
## 14.34 1.03 15.38
342390

343391
##### Compare outputs
344392

@@ -347,7 +395,7 @@ summary(pair_nba-pair_nba_2)
347395
```
348396

349397
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
350-
## 0 0 0 0 0 0 42
398+
## 0 0 0 0 0 0 31
351399

352400
#### Running time
353401

@@ -535,7 +583,7 @@ p<-ggplot()+
535583
p
536584
```
537585

538-
![](README_files/figure-markdown_github/unnamed-chunk-19-1.png)
586+
![](README_files/figure-markdown_github/unnamed-chunk-20-1.png)
539587

540588
Application 2 : Calculate the minimum travel time to the closest maternity ward in France
541589
-----------------------------------------------------------------------------------------
@@ -577,7 +625,7 @@ p<-ggplot()+
577625
p
578626
```
579627

580-
![](README_files/figure-markdown_github/unnamed-chunk-22-1.png)
628+
![](README_files/figure-markdown_github/unnamed-chunk-23-1.png)
581629

582630
Benchmark with other R packages
583631
===============================
@@ -604,7 +652,7 @@ system.time(
604652
```
605653

606654
## user system elapsed
607-
## 88.33 0.03 88.55
655+
## 85.13 0.02 85.14
608656

609657
``` r
610658
#dodgr
@@ -625,7 +673,7 @@ test_dodgr<-dodgr_dists(graph=data.frame(roads2),from=origin,to=destination,para
625673
```
626674

627675
## user system elapsed
628-
## 87.47 0.02 87.70
676+
## 86.38 0.05 86.46
629677

630678
``` r
631679
#cppRouting
@@ -635,7 +683,7 @@ test_cpp<-get_distance_matrix(graph,origin,destination,allcores = FALSE)
635683
```
636684

637685
## user system elapsed
638-
## 59.32 0.42 59.80
686+
## 57.71 0.23 58.00
639687

640688
#### Ouput
641689

@@ -661,7 +709,7 @@ test_dodgr<-dodgr_dists(graph=data.frame(roads2),from=origin,to=destination,para
661709
```
662710

663711
## user system elapsed
664-
## 126.10 0.59 33.43
712+
## 126.19 0.59 32.95
665713

666714
``` r
667715
#cppRouting
@@ -671,7 +719,7 @@ test_cpp<-get_distance_matrix(graph,origin,destination,allcores = TRUE)
671719
```
672720

673721
## user system elapsed
674-
## 0.14 0.05 22.36
722+
## 0.16 0.05 22.09
675723

676724
Benchmark on computing shortest paths by pairs
677725
----------------------------------------------
@@ -687,7 +735,7 @@ test_dodgr<-dodgr_paths(graph=data.frame(roads2),from=origin,to=destination,pair
687735
```
688736

689737
## user system elapsed
690-
## 531.79 18.66 551.76
738+
## 518.03 18.07 536.19
691739

692740
``` r
693741
#cppRouting
@@ -699,7 +747,7 @@ test_cpp<-get_path_pair(graph,origin,destination,algorithm = "NBA",constant=110/
699747
## Running NBA* ...
700748

701749
## user system elapsed
702-
## 4.93 0.36 5.29
750+
## 4.77 0.39 5.16
703751

704752
### Test similarity of the first travel
705753

@@ -726,5 +774,4 @@ setdiff(test_dodgr[[1]][[1]],test_cpp[[1]])
726774
New algorithms `cppRouting` will provide in the future
727775
======================================================
728776

729-
- Detours admitting shortest paths : finding the nodes that are reachable under a fixed detour time around the shortest path
730777
- Contraction hierarchies implementation

0 commit comments

Comments
 (0)