{"id":25897075,"url":"https://github.com/vlarmet/cpprouting","last_synced_at":"2025-03-17T16:09:42.293Z","repository":{"id":55869309,"uuid":"189749868","full_name":"vlarmet/cppRouting","owner":"vlarmet","description":"Algorithms for Routing and Solving the Traffic Assignment Problem","archived":false,"fork":false,"pushed_at":"2024-03-19T14:02:51.000Z","size":25505,"stargazers_count":102,"open_issues_count":10,"forks_count":9,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-26T13:22:29.829Z","etag":null,"topics":["algorithm","algorithm-b","bidirectional-a-star-algorithm","c-plus-plus","contraction-hierarchies","dijkstra-algorithm","distance","frank-wolfe","isochrones","parallel-computing","r","rcpp","shortest-paths","traffic-assignment"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vlarmet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-01T15:41:12.000Z","updated_at":"2024-06-18T09:10:04.089Z","dependencies_parsed_at":"2024-06-18T09:20:50.754Z","dependency_job_id":null,"html_url":"https://github.com/vlarmet/cppRouting","commit_stats":{"total_commits":148,"total_committers":1,"mean_commits":148.0,"dds":0.0,"last_synced_commit":"318805cdc4cbf60310e43e85cf02cfa54a6a650b"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vlarmet%2FcppRouting","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vlarmet%2FcppRouting/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vlarmet%2FcppRouting/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vlarmet%2FcppRouting/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vlarmet","download_url":"https://codeload.github.com/vlarmet/cppRouting/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244066179,"owners_count":20392406,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithm","algorithm-b","bidirectional-a-star-algorithm","c-plus-plus","contraction-hierarchies","dijkstra-algorithm","distance","frank-wolfe","isochrones","parallel-computing","r","rcpp","shortest-paths","traffic-assignment"],"created_at":"2025-03-02T23:17:47.929Z","updated_at":"2025-03-17T16:09:42.283Z","avatar_url":"https://github.com/vlarmet.png","language":"C++","readme":"# Support Our Open Source Project\r\n\r\nCreating and maintaining an open source project takes significant time and effort. If you find value in our work and would like to see it continue to grow and improve, please consider making a donation. Your support helps us cover hosting costs, development tools, and allows us to dedicate more time to enhancing this project. Every contribution, no matter how small, makes a big difference. Thank you for supporting open source!  \r\n\r\n[\u003cimg src=\"https://www.scclive.org/wp-content/uploads/2017/03/PayPal-Donate-Button.png\" width=\"150\"\u003e](https://www.paypal.com/donate/?hosted_button_id=SNBP8ZWMMYCDA)\r\n\r\ncppRouting v3 : Algorithms for Routing and Solving the Traffic\r\nAssignment Problem\r\n================\r\nVincent LARMET\r\nNovember 24, 2022\r\n\r\n-   [Package presentation](#package-presentation)\r\n-   [Installation](#installation)\r\n    -   [Stable version from CRAN](#stable-version-from-cran)\r\n    -   [or from github](#or-from-github)\r\n-   [What are we talking about ?](#what-are-we-talking-about-)\r\n-   [Readme Data](#readme-data)\r\n    -   [Set the number of threads used by\r\n        `cppRouting`](#set-the-number-of-threads-used-by-cpprouting)\r\n    -   [Instantiate the graph](#instantiate-the-graph)\r\n-   [Main functions](#main-functions)\r\n-   [Routing](#routing)\r\n    -   [Algorithms](#algorithms)\r\n    -   [Compute isochrones](#compute-isochrones)\r\n    -   [Compute possible detours within a fixed additional\r\n        cost](#compute-possible-detours-within-a-fixed-additional-cost)\r\n    -   [Contraction hierarchies](#contraction-hierarchies)\r\n    -   [Work with dual weighted\r\n        network](#work-with-dual-weighted-network)\r\n-   [Network simplification](#network-simplification)\r\n-   [Traffic assignment](#traffic-assignment)\r\n    -   [All-or-Nothing (AON)](#all-or-nothing-aon)\r\n        -   [Choosing the best routing\r\n            algorithm](#choosing-the-best-routing-algorithm)\r\n    -   [User Equilibrium (UE)](#user-equilibrium-ue)\r\n        -   [Link-based algorithms](#link-based-algorithms)\r\n        -   [Bush-based algorithms](#bush-based-algorithms)\r\n        -   [Performance comparison](#performance-comparison-1)\r\n-   [Algorithm compatibility](#algorithm-compatibility)\r\n-   [Parallel implementation](#parallel-implementation)\r\n-   [Applications](#applications)\r\n    -   [Application 1 : Calculate Two Step Floating Catchment Areas\r\n        (2SFCA) of general practitioners in\r\n        France](#application-1--calculate-two-step-floating-catchment-areas-2sfca-of-general-practitioners-in-france)\r\n    -   [Application 2 : Calculate the minimum travel time to the\r\n        closest maternity ward in\r\n        France](#application-2--calculate-the-minimum-travel-time-to-the-closest-maternity-ward-in-france)\r\n    -   [Application 3 : Calculate average commuting time to go to job\r\n        in\r\n        France](#application-3--calculate-average-commuting-time-to-go-to-job-in-france)\r\n    -   [Application 4 : Calculate the flow of people crossing each\r\n        municipality in the context of commuting in\r\n        Bourgogne-Franche-Comte\r\n        region](#application-4--calculate-the-flow-of-people-crossing-each-municipality-in-the-context-of-commuting-in-bourgogne-franche-comte-region)\r\n-   [Benchmark with other R packages](#benchmark-with-other-r-packages)\r\n-   [Citation](#citation)\r\n\r\n# Package presentation\r\n\r\n`cppRouting` is an `R` package which provide **routing** algorithms\r\n(shortest paths/distances, isochrones) and **traffic assignment**\r\nsolvers on non-negative weighted graphs.  \r\n`cppRouting` is characterized by :\r\n\r\n-   its ability to work on large road graphs (country/continent scale)\r\n-   its large choice of `one-to-one` shortest path algorithms\r\n-   its implementation of **contraction hierarchies** and associated\r\n    routing algorithms\r\n-   its large choice of algorithms for solving the **traffic assignment\r\n    problem (TAP)**\r\n-   its high performance through memory usage and parallel programming\r\n\r\n`cppRouting` is therefore particularly adapted for geographer, or\r\nwhoever who need to calculate accessibility indicators at large scale.  \r\nAll algorithms are written in C++ and mainly use containers from the\r\nStandard Template Library (STL).  \r\nThis package have been made with `Rcpp` and `RcppParallel` packages.\r\n\r\n# Installation\r\n\r\n### Stable version from CRAN\r\n\r\n``` r\r\ninstall.packages(\"cppRouting\")\r\n```\r\n\r\n### or from github\r\n\r\n``` r\r\nlibrary(remotes)\r\nremotes::install_github(\"vlarmet/cppRouting\")\r\n```\r\n\r\n# What are we talking about ?\r\n\r\n`cppRouting` implements algorithms belonging to **graph theory**, so\r\nlet’s define what a graph is.  \r\nA graph is commonly used to represent a network, which is composed of\r\nvertices connected by edges.\r\n\r\n![](README_files/figure-gfm/example_graph.png)\u003c!-- --\u003e\r\n\r\nIn `cppRouting`, an edge has at least three attributes : vertice’s ID\r\nfrom which it start, vertice’s ID from which it end and a weight\r\n(length, flow, travel time …).\r\n\r\n# Readme Data\r\n\r\n**This README file and all time measurements were made on a Windows 10\r\ncomputer, with 11th generation i5 (6 cores) processor and 32GB of\r\nmemory. **  \r\nThe data presented here is the official french road network describing\r\nover 500000 km of roads.  \r\nAll data used in this README are free and can be downloaded here :\r\n\r\n-   roads : \u003chttp://professionnels.ign.fr/route500\u003e  \r\n-   general practitioners location :\r\n    \u003chttps://www.insee.fr/fr/statistiques/3568614?sommaire=3568656#consulter\u003e  \r\n-   maternity wards location :\r\n    \u003chttps://www.insee.fr/fr/statistiques/3568611?sommaire=3568656#dictionnaire\u003e  \r\n-   shapefile of the \\~36000 communes in France :\r\n    \u003chttp://professionnels.ign.fr/adminexpress\u003e  \r\n-   commuting to work from the French national census :\r\n    \u003chttps://www.insee.fr/fr/statistiques/3566477#consulter\u003e\r\n\r\nGraph data have been preprocessed for more readability (see\r\ndata_preparation.R).\r\n\r\nThe final graph is composed of 234615 nodes and 685118 edges.  \r\nData has to be a 3 columns data.frame or matrix containing from, to and\r\na cost/distance column. Here the cost is the time needed to travel in\r\neach edges (in minutes). From and to are vertices IDs (character or\r\nnumeric).\r\n\r\n``` r\r\nlibrary(cppRouting)\r\nlibrary(dplyr)\r\nlibrary(sf)\r\nlibrary(ggplot2)\r\nlibrary(concaveman)\r\nlibrary(ggmap)\r\nlibrary(tmap)\r\nlibrary(microbenchmark)\r\nlibrary(reshape2)\r\nlibrary(kableExtra)\r\n\r\n#Reading french road data\r\nroads  \u003c-  read.csv(\"roads.csv\",colClasses = c(\"character\",\"character\",\"numeric\"))\r\n#Shapefile data of communes (polygons)\r\ncom  \u003c-  read_sf(\"com_simplified_geom.shp\")\r\n#Correspondance file between communes and nodes in the graph (nearest node to each commune centroid)\r\nndcom  \u003c-  read.csv(\"node_commune.csv\",colClasses = c(\"character\",\"character\",\"numeric\"))\r\n#General practitioners locations\r\nmed  \u003c-  read.csv(\"doctor.csv\",colClasses = c(\"character\",\"numeric\",\"character\",\"numeric\"))\r\n#Import materinty ward locations\r\nmaternity  \u003c-  read.csv(\"maternity.csv\",colClasses = c(\"character\",\"numeric\"))\r\n#Commuting data from national census\r\nload(\"commuting.Rds\")\r\n#Import nodes coordinates (projected in EPSG : 2154)\r\ncoord  \u003c-  read.csv(\"coordinates.csv\",colClasses = c(\"character\",\"numeric\",\"numeric\"))\r\n```\r\n\r\n#### Head of road network data\r\n\r\n``` r\r\nhead(roads)\r\n```\r\n\r\n    ##   from     to    weight\r\n    ## 1    0 224073 0.4028571\r\n    ## 2    1  65036 3.5280000\r\n    ## 3    2 173723 1.8480000\r\n    ## 4    3      2 2.5440000\r\n    ## 5    4 113129 4.9680000\r\n    ## 6    5      4 1.6680000\r\n\r\n#### Head of coordinates data\r\n\r\n``` r\r\nhead(coord)\r\n```\r\n\r\n    ##   ID        X       Y\r\n    ## 1  0 805442.8 6458384\r\n    ## 2  1 552065.9 6790520\r\n    ## 3  2 556840.2 6790475\r\n    ## 4  3 554883.7 6790020\r\n    ## 5  4 548345.2 6791000\r\n    ## 6  5 547141.3 6790434\r\n\r\n### Set the number of threads used by `cppRouting`\r\n\r\n``` r\r\nRcppParallel::setThreadOptions(numThreads = 1)\r\n```\r\n\r\n### Instantiate the graph\r\n\r\n``` r\r\n#Instantiate a graph with coordinates\r\ngraph  \u003c-  makegraph(roads, directed = T, coords = coord)\r\n```\r\n\r\nGraph object have some useful attributes for the user :  \r\n- `graph$nbnode` : total number of vertices,  \r\n- `graph$dict$ref` : vertices ids.\r\n\r\nOther attributes are internals data and have no interest for the user.\r\nAll graph attributes should **never** be modified by the user.\r\n\r\n# Main functions\r\n\r\n`cppRouting` package provide these functions :\r\n\r\n-   `get_distance_matrix` : compute distance matrix (between all\r\n    combinations origin-destination nodes - *one-to-many*),  \r\n-   `get_distance_pair` : compute distances between origin and\r\n    destination by pair (*one-to-one*),  \r\n-   `get_path_pair` : compute shortest paths between origin and\r\n    destination by pair (*one-to-one*),  \r\n-   `get_multi_paths` : compute shortest paths between all origin nodes\r\n    and all destination nodes (*one-to-many*),  \r\n-   `get_isochrone` : compute isochrones/isodistances with one or\r\n    multiple breaks.  \r\n-   `get_detour` : return nodes that are reachable within a fixed\r\n    additional cost around shortest paths. This function can be useful\r\n    in producing accessibility indicators.  \r\n-   `cpp_simplify` : remove non-intersection nodes, duplicated edges and\r\n    isolated loops in the graph. Graph topology is preserved so distance\r\n    calculation is faster and remains true. This function can be applied\r\n    to very large graphs (several millions of nodes).  \r\n-   `cpp_contract` : contract the graph by applying **contraction\r\n    hierarchies** algorithm.  \r\n-   `get_aon` : given an origin-destination matrix, compute\r\n    All-or-Nothing assignment.  \r\n-   `assign_traffic` : given an origin-destination matrix, estimate the\r\n    traffic flows on the network.\r\n\r\n# Routing\r\n\r\nAs the package name suggest, `cppRouting` is initially aimed to provide\r\nefficient algorithms for finding shortest paths.\r\n\r\n## Algorithms\r\n\r\nPath algorithms proposed by the package are :\r\n\r\n-   **1** uni-directional Dijkstra algorithm,\r\n-   **2** bi-directional Dijkstra algorithm,\r\n-   **3** uni-directional A\\* algorithm  \r\n-   **4** New bi-directional A\\* algorithm (Piljs \u0026 Post, 2009 : see\r\n    \u003chttp://repub.eur.nl/pub/16100/ei2009-10.pdf\u003e)\r\n-   **5** *one-to-one* bi-directional Dijkstra adapted to contraction\r\n    hierarchies (Geisberger \u0026 al., 2008)\r\n-   **6** *many-to-many* bi-directional Dijkstra adapted to contraction\r\n    hierarchies (Geisberger \u0026 al., 2008)\r\n-   **7** PHAST algorithm (Hardware-accelerated shortest path trees),\r\n    *one-to-all* algorithm adapted to contraction hierarchies (Delling \u0026\r\n    al., 2011)\r\n\r\n*1*, *2*, *3* and *4* are available for **one-to-one** calculation in\r\n`get_distance_pair` and `get_path_pair` functions on a\r\n**non-contracted** graph. In these functions, uni-directional Dijkstra\r\nalgorithm is stopped when the destination node is reached.  \r\n`A*` and `NBA*` are relevant if geographic coordinates of all nodes are\r\nprovided. Note that coordinates should be expressed in a **projection\r\nsystem**.  \r\nTo be accurate and efficient, `A*` and `NBA*` algorithms should use an\r\nadmissible heuristic function (here the Euclidean distance), i.e cost\r\nand heuristic function must be expressed in the same unit.  \r\nIn `cppRouting`, heuristic function `h` for a node (n) is defined such\r\nthat :  \r\n**h(n,d) = ED(n,d) / k** with *h* the heuristic, *ED* the Euclidean\r\ndistance, *d* the destination node and a constant *k*.  \r\nSo in the case where coordinates are expressed in meters and cost is\r\nexpressed in time, *k* is the maximum speed allowed on the road. By\r\ndefault, constant is 1 and is designed for graphs with cost expressed in\r\nthe same unit than coordinates (for example in meters).  \r\nIf coordinates cannot be provided, bi-directional Dijkstra algorithm is\r\nthe best option in terms of performance.\r\n\r\n*5* is used for **one-to-one** calculation in `get_distance_pair` and\r\n`get_path_pair` functions on a **contracted** graph.\r\n\r\n*1* is used for **one-to-many** calculation in `get_distance_matrix`\r\nfunction on a **non-contracted** graph.\r\n\r\n*6* and *7* are available for **one-to-many** calculation in\r\n`get_distance_matrix` function on a **contracted** graph.\r\n\r\nLet’s compare different path algorithms in terms of performance.  \r\nFor `A*` and `NBA` algorithms, coordinates are defined in meters and max\r\nspeed is 110km/h; so for the heuristic function to be admissible, we\r\nhave to convert meters to minutes by setting constant to 110/0.06 :\r\n\r\n``` r\r\n#Generate 2000 random origin and destination nodes\r\norigin  \u003c-  sample(graph$dict$ref,  2000)\r\ndestination  \u003c-  sample(graph$dict$ref,  2000)\r\nmicrobenchmark(dijkstra=pair_dijkstra  \u003c-  get_distance_pair(graph, origin, destination, algorithm = \"Dijkstra\"), \r\n               bidir=pair_bidijkstra  \u003c-  get_distance_pair(graph, origin, destination, algorithm = \"bi\"), \r\n               astar=pair_astar  \u003c-  get_distance_pair(graph, origin, destination, algorithm = \"A*\", constant = 110/0.06), \r\n               nba=pair_nba  \u003c-  get_distance_pair(graph, origin, destination, algorithm = \"NBA\", constant = 110/0.06), \r\n               times=1)\r\n```\r\n\r\n    ## Unit: seconds\r\n    ##      expr       min        lq      mean    median        uq       max neval\r\n    ##  dijkstra 28.648378 28.648378 28.648378 28.648378 28.648378 28.648378     1\r\n    ##     bidir 22.463780 22.463780 22.463780 22.463780 22.463780 22.463780     1\r\n    ##     astar 19.207150 19.207150 19.207150 19.207150 19.207150 19.207150     1\r\n    ##       nba  9.976939  9.976939  9.976939  9.976939  9.976939  9.976939     1\r\n\r\n#### Output\r\n\r\n``` r\r\nhead(cbind(pair_dijkstra,pair_bidijkstra,pair_astar,pair_nba))\r\n```\r\n\r\n    ##      pair_dijkstra pair_bidijkstra pair_astar pair_nba\r\n    ## [1,]      258.4616        258.4616   258.4616 258.4616\r\n    ## [2,]      464.5499        464.5499   464.5499 464.5499\r\n    ## [3,]      294.5504        294.5504   294.5504 294.5504\r\n    ## [4,]      302.7190        302.7190   302.7190 302.7190\r\n    ## [5,]      120.1393        120.1393   120.1393 120.1393\r\n    ## [6,]      578.0233        578.0233   578.0233 578.0233\r\n\r\nSo, how to choose the algorithm ? It’s simple, the faster, the better.\r\nIf coordinates are provided, go for `NBA`, else go for bidirectional\r\nDijkstra. Uni-directional Dijkstra and `A*` algorithms should be used if\r\nmain memory is (almost) full because they require only one graph instead\r\nof two (forward and backward).\r\n\r\n## Compute isochrones\r\n\r\nAn isochrone is a set of nodes reachable from a node within a fixed\r\nlimit.  \r\nLet’s compute isochrones around Dijon city\r\n\r\n``` r\r\n#Compute isochrones\r\niso  \u003c-  get_isochrone(graph, from = \"205793\", lim = c(15, 25, 45, 60, 90, 120))\r\n#Convert nodes to concave polygons with concaveman package\r\npoly  \u003c-  lapply(iso[[1]], function(x){\r\n  x  \u003c-  data.frame(noeuds=x, stringsAsFactors = F)\r\n  x  \u003c-  left_join(x, coord, by=c(\"noeuds\"=\"ID\"))\r\n  return(concaveman(summarise(st_as_sf(x, coords=c(\"X\", \"Y\"), crs=2154))))\r\n})\r\n\r\npoly  \u003c-  do.call(rbind, poly)\r\npoly$time  \u003c-  as.factor(names(iso[[1]]))\r\n#Multipolygon\r\npoly2  \u003c-  st_cast(poly, \"MULTIPOLYGON\")\r\npoly2$time  \u003c-  reorder(poly2$time, c(120, 90, 60, 45, 25, 15))\r\n#Reproject for plotting\r\npoly2  \u003c-  st_transform(poly2, \"+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs\")\r\n#Import map backgroung\r\n\r\ndijon   \u003c-   get_stamenmap(bbox = c(left = 1.708, \r\n                             bottom = 45.126, \r\n                             right = 8.003, \r\n                             top = 49.232),  maptype = \"toner-2010\",  zoom = 7)\r\n#Plot the map\r\np  \u003c-  ggmap(dijon)+\r\n  geom_sf(data=poly2, aes(fill=time), alpha=.8, inherit.aes = FALSE)+\r\n  scale_fill_brewer(palette = \"YlOrRd\")+\r\n  labs(fill=\"Minutes\")+\r\n  ggtitle(\"Isochrones around Dijon\")+\r\n  theme(axis.text.x = element_blank(), \r\n        axis.text.y = element_blank(), \r\n        axis.ticks = element_blank(), \r\n        axis.title.y=element_blank(), axis.title.x=element_blank())\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-9-1.png)\u003c!-- --\u003e\r\n\r\n## Compute possible detours within a fixed additional cost\r\n\r\n`get_detour` function returns all reachable nodes within a fixed detour\r\ntime around the shortest path between origin and destination nodes.\r\nReturned nodes (n) meet the following condition :  \r\n**SP(o,n) + SP(n,d) \\\u003c SP(o,d) + t**  \r\nwith *SP* shortest distance/time, *o* the origin node, *d* the\r\ndestination node and *t* the extra cost.  \r\nThe algorithm used is a slightly modified bidirectional Dijkstra.  \r\nLet’s see an example for the path between Dijon and Lyon city :\r\n\r\n``` r\r\n#Compute shortest path\r\ntrajet \u003c- get_path_pair(graph,from=\"205793\",to=\"212490\")\r\n\r\n#Compute shortest path\r\ndistance \u003c- get_distance_pair(graph,from=\"205793\",to=\"212490\")\r\n\r\n#Compute detour time of 25 and 45 minutes\r\ndet25 \u003c- get_detour(graph,from=\"205793\",to=\"212490\",extra=25)\r\ndet45 \u003c- get_detour(graph,from=\"205793\",to=\"212490\",extra=45)\r\n\r\n#Create sf object of nodes\r\npts \u003c- st_as_sf(coord,coords=c(\"X\",\"Y\"),crs=2154)\r\npts \u003c- st_transform(pts,crs=4326)\r\npts$time \u003c- ifelse(pts$ID %in% unlist(det45),\"45\",\"0\")\r\npts$time \u003c- ifelse(pts$ID %in% unlist(det25),\"25\",pts$time)\r\npts$time \u003c- ifelse(pts$ID %in% unlist(trajet),\"Shortest Path\",pts$time)\r\npts$time \u003c- factor(pts$time,levels = c(\"25\",\"45\",\"Shortest Path\",\"0\"))\r\n\r\n#Plot\r\ndijon   \u003c-   get_stamenmap(bbox = c(left = 3.2, \r\n                             bottom = 45.126, \r\n                             right = 6.5, \r\n                             top = 47.8),  maptype = \"toner-2010\",  zoom = 8)\r\n\r\np \u003c- ggmap(dijon)+\r\n  geom_sf(data=pts[pts$time!=\"0\",],aes(color=time),inherit.aes = FALSE)+\r\n  ggtitle(paste0(\"Detours around Dijon-lyon path - \",round(distance,digits = 2),\" minutes\"))+\r\n  labs(color=\"Minutes\")+\r\n  theme(axis.text.x = element_blank(),\r\n        axis.text.y = element_blank(),\r\n        axis.ticks = element_blank(),\r\n        axis.title.y=element_blank(),axis.title.x=element_blank())\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-10-1.png)\u003c!-- --\u003e\r\n\r\n## Contraction hierarchies\r\n\r\nContraction hierarchies is a speed-up technique for finding shortest\r\npath on a network. It was proposed by Geisberger \u0026 al.(2008).  \r\nInitially created for *one-to-one* queries, it has been extended to\r\n*many-to-many* and distance matrix calculation.  \r\nThis technique is composed of two phases:\r\n\r\n-   preprocessing phase called *contraction* with `cpp_contract`\r\n    function\r\n-   query phase : a slightly modified version of bidirectional search\r\n    for `one-to-one` query, available in `get_distance_pair` and\r\n    `get_path_pair`; PHAST algorithm and a `many-to-many` algorithm\r\n    using buckets available in `get_distance_matrix` function.\r\n\r\nContraction phase consists of iteratively removing a vertex **v** from\r\nthe graph and creating a shortcut for each pair **(u,w)** of **v**’s\r\nneighborhood if the shortest path from **u** to **w** contains **v**. To\r\nbe efficient and avoid creating too much shortcuts, vertices have to be\r\nordered according to several heuristics. The two heuristics used by\r\n`cppRouting` are :\r\n\r\n-   *edge difference* (number of shortcuts potentially created by\r\n    removing *v* - number of incoming edges - number of outcoming edges)\r\n-   *deleted neighbors* (number of already contracted neighbors)\r\n\r\nThe nodes are initially ordered using only *edge difference*, then\r\nimportance of *v* is *lazily* updated during contraction phase with the\r\ncombined two heuristics. To see more detailed explanations, see these\r\nressources :\r\n\r\n-   [quick\r\n    review](https://pdfs.semanticscholar.org/3871/1351fa5749714370786ed17565e478c459d7.pdf)\r\n-   [authors\r\n    article](http://algo2.iti.kit.edu/schultes/hwy/contract.pdf)\r\n-   [detailed author\r\n    thesis](http://algo2.iti.kit.edu/documents/routeplanning/geisberger_dipl.pdf)\r\n\r\n``` r\r\n#Contraction of input graph\r\ngraph3 \u003c- cpp_contract(graph, silent=TRUE)\r\n\r\n#Calculate distances on the contracted graph\r\nsystem.time(\r\n  pair_ch \u003c- get_distance_pair(graph3, origin, destination)\r\n)\r\n```\r\n\r\n    ## utilisateur     système      écoulé \r\n    ##        0.27        0.01        0.28\r\n\r\n#### Compare outputs\r\n\r\n``` r\r\nsummary(pair_ch-pair_dijkstra)\r\n```\r\n\r\n    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's \r\n    ##       0       0       0       0       0       0      38\r\n\r\n#### Performance comparison\r\n\r\n##### Distance by pair\r\n\r\nHere are the measurements of contraction time and query time (in second)\r\nof contraction hierarchies on different graphs :\r\n\u003ctable class=\"table\" style=\"width: auto !important; margin-left: auto; margin-right: auto;\"\u003e\r\n\u003cthead\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"empty-cells: hide;border-bottom:hidden;\" colspan=\"5\"\u003e\r\n\u003c/th\u003e\r\n\u003cth style=\"border-bottom:hidden;padding-bottom:0; padding-left:3px;padding-right:3px;text-align: center; \" colspan=\"6\"\u003e\r\n\r\n\u003cdiv style=\"border-bottom: 1px solid #ddd; padding-bottom: 5px; \"\u003e\r\n\r\nNumber of queries\r\n\r\n\u003c/div\u003e\r\n\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nGraph\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nVertices\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nEdges\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\npreprocessing\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nalgorithm\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n1000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n2000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n5000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n10000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n100000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n1000000\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003c/thead\u003e\r\n\u003ctbody\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n234615\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n685118\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n14\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n0.22\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n0.25\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n0.44\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n0.64\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n4.60\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n45.86\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n234615\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n685118\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n14\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nbi\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n11.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n22.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n55.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n110.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1100.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n11000.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n234615\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n685118\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n14\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nnba\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n5.05\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n10.10\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n25.25\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n50.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n505.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n5050.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n4559270\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n10389741\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n154\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n4.08\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n3.92\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n4.66\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n5.11\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n18.20\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n147.05\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n4559270\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n10389741\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n154\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nbi\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n309.40\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n618.80\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1547.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n3094.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n30940.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n309400.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n4559270\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n10389741\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n154\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nnba\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n144.80\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n289.60\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n724.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1448.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n14480.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n144800.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n16210743\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n36890020\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n536\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n13.44\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n13.61\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n14.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n15.91\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n35.14\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n227.22\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n16210743\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n36890020\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n536\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nbi\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1159.20\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n2318.40\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n5796.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n11592.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n115920.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1159200.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n16210743\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n36890020\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n536\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nnba\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n630.60\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1261.20\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n3153.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n6306.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n63060.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n630600.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tbody\u003e\r\n\u003ctfoot\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e ch : bidirectional search on the contracted graph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e bi : bidirectional search on the original graph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e nba : new bidirectional A\\* on the original graph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tfoot\u003e\r\n\u003c/table\u003e\r\n\r\nHere are the plots (in log-log) of query time improvement factor of *one\r\nto one CH* algorithm compared to bidirectional Dijkstra and NBA :  \r\n![](README_files/figure-gfm/unnamed-chunk-14-1.png)\u003c!-- --\u003e\r\n\r\nAs we can see on the plot, the larger is the graph, the higher is the\r\nbenefit of using contraction hierarchies. For OSM Europe, query time can\r\nbe faster by a factor of **5000** compared to bidirectional Dijkstra and\r\n**2500** to NBA.\r\n\r\n##### Distance matrix\r\n\r\nHere are the measurements of query time (in second) of contraction\r\nhierarchies on different graphs.  \r\nWe compare *PHAST* and *many to many CH* to Dijkstra algorithm on square\r\nmatrix (i.e the sets of source and target nodes are of equal length).\r\n\r\n\u003ctable class=\"table\" style=\"width: auto !important; margin-left: auto; margin-right: auto;\"\u003e\r\n\u003cthead\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"empty-cells: hide;border-bottom:hidden;\" colspan=\"2\"\u003e\r\n\u003c/th\u003e\r\n\u003cth style=\"border-bottom:hidden;padding-bottom:0; padding-left:3px;padding-right:3px;text-align: center; \" colspan=\"4\"\u003e\r\n\r\n\u003cdiv style=\"border-bottom: 1px solid #ddd; padding-bottom: 5px; \"\u003e\r\n\r\n\\|S\\|=\\|T\\|\r\n\r\n\u003c/div\u003e\r\n\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nGraph\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\nalgorithm\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n1000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n2000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n5000\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:center;\"\u003e\r\n10000\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003c/thead\u003e\r\n\u003ctbody\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nmch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n0.64\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n1.31\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n5.19\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n19.31\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nphast\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n4.21\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n8.42\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n21.05\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n42.10\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nROUTE 500\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nDijkstra\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n27.45\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n54.90\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n137.25\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n274.50\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nmch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n11.15\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n18.66\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n46.13\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n102.18\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nphast\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n100.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n201.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n502.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1005.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nDijkstra\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n781.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n1563.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n3907.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n7815.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nmch\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n39.17\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n64.50\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n144.39\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\n291.28\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nphast\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n460.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n920.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n2300.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n4600.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:center;font-weight: bold;\"\u003e\r\nOSM Europe\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\nDijkstra\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n2886.40\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n5772.80\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n14432.00\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:center;\"\u003e\r\n28864.00\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tbody\u003e\r\n\u003ctfoot\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e mch : many-to-many bidirectional search on the contracted\r\ngraph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e phast : phast algorithm on the contracted graph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e Dijkstra : Dijkstra search on the original graph\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e \\|S\\| : number of source nodes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"padding: 0; border:0;\" colspan=\"100%\"\u003e\r\n\u003csup\u003e\u003c/sup\u003e \\|T\\| : number of target nodes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tfoot\u003e\r\n\u003c/table\u003e\r\n\r\nHere are the plots (in log-log) of query time improvement factor of\r\n*PHAST* and *many to many CH* compared to Dijkstra algorithm :\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-16-1.png)\u003c!-- --\u003e\r\n\r\nBenefits are less important than *one-to-one* queries but still\r\ninteresting. For OSM Europe, query time can be faster by a factor of\r\n**100*.  \r\nPHAST’s improvement is constant since it iteratively perform an\r\n*one-to-all\\* search, just like original Dijkstra.  \r\n*many to many CH* is well adapted for **square matrix\\*\\*.\r\n\r\nHere are the plots of query time of *PHAST* and *many to many CH* on\r\nassymetric matrix (i.e. number of source and number of target are\r\nunequal) with *\\|S\\| / \\|T\\|* the number of sources divided by the\r\nnumber of targets :\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-17-1.png)\u003c!-- --\u003e\r\n\r\nNote that this plot is the same for *\\|T\\| / \\|S\\|*.  \r\n*PHAST* algorithm is much faster for rectangular matrix. The rate *\\|S\\|\r\n/ \\|T\\|* where *many to many CH* is better varies according the graph\r\nsize. For example, if we have to calculate a distance matrix between\r\n10000 sources and 10 targets (or 10 sources and 10000 targets) on OSM\r\nFrance, we must use *PHAST*. On the other hand, if we want a matrix of\r\n10000 sources and 8000 targets, we use *many to many CH* algorithm.\r\n\r\n## Work with dual weighted network\r\n\r\nSometimes it can be useful to sum up additional weights along the\r\nshortest path. For a use-case example, let’s say we want to compute the\r\ndistance along the shortest time path or the time needed to travel the\r\nshortest distance path. It is now possible to set an auxiliary set of\r\nedge weights during graph construction in `makegraph()` function and set\r\n`aggregate_aux` to `TRUE` in `get_distance_*` functions.  \r\nLet’s see an example where we would like to compute the number of edges\r\nwithin each shortest path :  \r\n\r\n``` r\r\n# The weight to be minimized is set in 'df' argument. Additional weights are set in \"aux\"\r\n# We set auxiliary weights to 1 in order to count number of edge in shortest paths\r\ndgr \u003c- makegraph(df = roads, directed = TRUE, coords = coord, aux = 1) \r\n\r\n# Compute number of edge\r\nhops \u003c- get_distance_pair(Graph = dgr, from = origin, to = destination, aggregate_aux = TRUE)\r\n\r\n# plot\r\ndfp \u003c- data.frame(n_edges = hops, travel_time = pair_nba)\r\np \u003c- ggplot(dfp, aes(x = travel_time, y = n_edges))+\r\n  geom_point()+\r\n  labs(x = \"Travel time (min)\", y = \"Number of edge\")+\r\n  theme_bw()\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-18-1.png)\u003c!-- --\u003e\r\n\r\nNote that this functionality work for **contracted graphs** as well.\r\n\r\n# Network simplification\r\n\r\n`cpp_simplify`’s internal function performs two major steps :  \r\n- removing non-intersection nodes between two intersection nodes then\r\ncalculate cost of the new edges,  \r\n- removing duplicate edges that are potentially created in the first\r\nstep.\r\n\r\nIn order to remove maximum number of nodes, some iterations are needed\r\nuntil only intersection nodes are remained.\r\n\r\nLet’s see a small example :\r\n\r\n``` r\r\nlibrary(igraph)\r\n#Create directed graph\r\nedges\u003c-data.frame(from=c(\"a\",\"b\",\"c\",\"d\",\r\n                         \"d\",\"e\",\"e\",\"e\",\r\n                         \"f\",\"f\",\"g\",\"h\",\"h\",\"h\",\r\n                         \"i\",\"j\",\"k\",\"k\",\"k\",\r\n                         \"l\",\"l\",\"l\",\"m\",\"m\",\"m\",\r\n                         \"n\",\"n\",\"o\",\"p\",\"q\",\"r\"),\r\n                  to=c(\"b\",\"c\",\"d\",\"e\",\"k\",\"f\",\"d\",\r\n                       \"h\",\"g\",\"e\",\"f\",\"e\",\"i\",\"k\",\r\n                       \"j\",\"i\",\"h\",\"d\",\"l\",\"k\",\r\n                       \"m\",\"n\",\"n\",\"o\",\"l\",\"l\",\"m\",\"m\",\r\n                       \"r\",\"p\",\"q\"),\r\n                  dist=rep(1,31))\r\n\r\n#Plotting with igraph\r\npar(mfrow=c(1,2),mar=c(3,0,3,0))\r\n\r\nigr1\u003c-graph_from_data_frame(edges)\r\nset.seed(2)\r\nplot.igraph(igr1,edge.arrow.size=.3,main=\"Original graph\")\r\nbox(col=\"black\")\r\n\r\n#Instantiate cppRouting graph, then simplify without iterations\r\ngraph_ex\u003c-makegraph(edges,directed = TRUE)\r\nsimp\u003c-cpp_simplify(graph_ex,rm_loop = FALSE)\r\n#Convert graph to df\r\nedges2\u003c-to_df(simp)\r\n\r\n#Plotting simplified graph\r\nigr2\u003c-graph_from_data_frame(edges2)\r\nset.seed(20)\r\nplot(igr2,edge.arrow.size=.3,edge.label=E(igr2)$dist,main=\"One iteration - keeping loop\")\r\nbox(col=\"black\")\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-19-1.png)\u003c!-- --\u003e\r\n\r\nHere, junction nodes are `e`, `h`, `d`, `k`, `l`, `i` and `m`. So `b`,\r\n`c`, `f` and `n` have been contracted in the first step of the function.\r\nBy contracting `n`, an edge with cost of 2 has been created between `m`\r\nand `l` nodes.  \r\nThe second step of the function has removed this edge which is greater\r\nthan the original one (i.e 1), and the whole process now need a second\r\niteration to remove `m` and `l` that aren’t intersection nodes\r\nanymore.  \r\nLet’s try with `iterate` argument set to `TRUE` :\r\n\r\n``` r\r\npar(mfrow=c(1,2),mar=c(3,0,3,0))\r\n#Simplify with iterations\r\nsimp2\u003c-cpp_simplify(graph_ex,rm_loop = FALSE,iterate = TRUE)\r\nedges3\u003c-to_df(simp2)\r\nigr3\u003c-graph_from_data_frame(edges3)\r\nset.seed(2)\r\nplot(igr3,edge.arrow.size=.3,edge.label=E(igr3)$dist,main=\"Second iteration - keeping loop\")\r\nbox(col=\"black\")\r\n\r\n#The same but removing loops\r\nsimp3\u003c-cpp_simplify(graph_ex,rm_loop = TRUE,iterate = TRUE)\r\nedges4\u003c-to_df(simp3)\r\n\r\nigr4\u003c-graph_from_data_frame(edges4)\r\nset.seed(2)\r\nplot(igr4,edge.arrow.size=.3,edge.label=E(igr4)$dist,main=\"Second iteration - removing loop\")\r\nbox(col=\"black\")\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-20-1.png)\u003c!-- --\u003e\r\n\r\n#### French road network simplification\r\n\r\n``` r\r\n#Simplify original graph by keeping nodes of interest\r\ngraph2\u003c-cpp_simplify(graph,\r\n                     iterate = TRUE,\r\n                     keep = unique(c(origin,destination))) #we don't want to remove origin and destination nodes\r\n\r\n#Running NBA*\r\nsystem.time(\r\n  pair_nba_2\u003c-get_distance_pair(graph2,origin,destination,algorithm = \"NBA\",constant = 110/0.06)\r\n)\r\n```\r\n\r\n    ## utilisateur     système      écoulé \r\n    ##        8.12        0.01        8.14\r\n\r\n##### Compare outputs\r\n\r\n``` r\r\nsummary(pair_nba-pair_nba_2)\r\n```\r\n\r\n    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's \r\n    ##       0       0       0       0       0       0      38\r\n\r\n#### Running time\r\n\r\nHere are running times in *second* on graphs of different sizes :  \r\n- data presented here,  \r\n- OpenStreetMap french road network from\r\n[Geofabrik](https://download.geofabrik.de/),  \r\n- an assembly of several european countries from OSM (France, Italy,\r\nSpain, Germany, Portugal, Switzerland, Belgium and Netherlands).\r\n\r\nOpenStreetMap data have been extracted with [osm2po](https://osm2po.de/)\r\ntool, which I highly recommend.\r\n\r\n\u003ctable\u003e\r\n\u003cthead\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\nNetwork\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nNodes\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nEdges\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nRuntime without iterations\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nRemoved nodes in first iteration\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nRuntime with iterations\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nNumber of iteration\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nTotal removed nodes\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:right;\"\u003e\r\nRemoved nodes percentage\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003c/thead\u003e\r\n\u003ctbody\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;\"\u003e\r\nREADME data\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n234,615\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n685,118\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n0.47\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n39,737\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n0.51\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n4\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n41,843\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n17.83\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;\"\u003e\r\nOSM France\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n4,559,270\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n10,389,741\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n6.73\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n818,096\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n11.62\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n9\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n842,252\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n18.47\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;\"\u003e\r\nOSM ‘Europe’\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n16,210,743\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n36,890,020\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n27.94\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n3,365,240\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n46.47\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n11\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n3,465,724\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:right;\"\u003e\r\n21.38\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tbody\u003e\r\n\u003c/table\u003e\r\n\r\n# Traffic assignment\r\n\r\nTraffic assignment models are used to estimate the traffic flows on a\r\nnetwork. It take as input origin-destinations matrix describing volume\r\nbetween each OD pair.  \r\nFor this part, we will use standard networks massively used in traffic\r\nmodelling studies : Siouxfalls and Chicago networks\r\n([source](https://github.com/bstabler/TransportationNetworks)).  \r\nLet’s import Siouxfalls. It is composed of 24 nodes and 76 links.\r\n\r\n``` r\r\nnet \u003c- read.csv(\"siouxfalls_net.csv\")\r\nhead(net)\r\n```\r\n\r\n    ##   Init.node Term.node  Capacity Length Free.Flow.Time    B Power Speed.limit\r\n    ## 1         1         2 25900.201      6              6 0.15     4           0\r\n    ## 2         1         3 23403.473      4              4 0.15     4           0\r\n    ## 3         2         1 25900.201      6              6 0.15     4           0\r\n    ## 4         2         6  4958.181      5              5 0.15     4           0\r\n    ## 5         3         1 23403.473      4              4 0.15     4           0\r\n    ## 6         3         4 17110.524      4              4 0.15     4           0\r\n    ##   Toll Type\r\n    ## 1    0    1\r\n    ## 2    0    1\r\n    ## 3    0    1\r\n    ## 4    0    1\r\n    ## 5    0    1\r\n    ## 6    0    1\r\n\r\nWe also have OD matrix as well :\r\n\r\n``` r\r\ntrips \u003c- read.csv(\"siouxfalls_trips.csv\")\r\nhead(trips)\r\n```\r\n\r\n    ##   from to demand\r\n    ## 1    1  2    100\r\n    ## 2    1  3    100\r\n    ## 3    1  4    500\r\n    ## 4    1  5    200\r\n    ## 5    1  6    300\r\n    ## 6    1  7    500\r\n\r\n## All-or-Nothing (AON)\r\n\r\nAll-or-Nothing assignment (AON) is the most simplistic (and fastest)\r\nmethod to load flow on a network, since it assume there is no congestion\r\neffects. The assignment algorithm itself is the procedure that loads the\r\norigin-destination matrix to the shortest path trees and produces the\r\nflows.  \r\nIn `cppRouting`, OD matrix is represented as 3 vectors of equal length\r\n:  \r\n- `from` : origin node,  \r\n- `to` : destination node,  \r\n- `demand` : volume.\r\n\r\nLet’s load flows on the network using `get_aon()` function :\r\n\r\n``` r\r\nsgr \u003c- makegraph(df = net[,c(\"Init.node\", \"Term.node\", \"Free.Flow.Time\")], directed = TRUE) \r\n\r\n# Compute AON assignment using OD trips\r\naon \u003c- get_aon(Graph = sgr, from = trips$from, to = trips$to, demand = trips$demand)\r\nhead(aon)\r\n```\r\n\r\n    ##   from to cost  flow\r\n    ## 1    1  2    6  3800\r\n    ## 2    1  3    4  6000\r\n    ## 3    2  1    6  3800\r\n    ## 4    2  6    5  6600\r\n    ## 5    3  1    4  6000\r\n    ## 6    3  4    4 10200\r\n\r\n``` r\r\ng \u003c- graph_from_data_frame(aon[,c(\"from\", \"to\", \"flow\")], directed = TRUE)\r\nc_scale \u003c- colorRamp(c('#60AF66', 'yellow', 'red'))\r\nmaxflow \u003c- max(c(aon$flow))\r\n\r\nE(g)$color = apply(c_scale(aon$flow/maxflow), 1, function(x) rgb(x[1]/255,x[2]/255,x[3]/255) )\r\nset.seed(5)\r\nplot(g, edge.width = net$Capacity/2000, edge.arrow.size = 0, main = \"AON assignment\")\r\nbox(col = \"black\")\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-27-1.png)\u003c!-- --\u003e\r\n\r\n`get_aon` work also for **contracted networks**.\r\n\r\n### Choosing the best routing algorithm\r\n\r\nComputing time is directly linked to shortest paths calculations. The\r\nuser has the choice between two kind of routing algorithms, depending\r\nthe **sparsity** of OD matrix. By default, `algorithm` argument is set\r\nto bidirectional Dijkstra. Just like `get_*_pair` functions, it run\r\n`length(from)` times routing algorithm for finding each OD pair’s\r\nshortest path. If OD matrix is dense, recursive *one-to-many* methods\r\nlike Dijkstra algorithm would be preferred.  \r\nGiven an origin node, bidirectional Dijkstra et NBA\\* algorithms are on\r\naverage 2 and 5 times faster than Dijkstra algorithm, respectively.\r\n\r\nThis rule-of-thumb apply also when the network have been contracted\r\nusing `cpp_contract` function. `phast` algorithm is for matrix-like\r\nshortest paths calculation, and `bi` is pairwise. In that case, `bi`\r\nalgorithm is 200 times faster than `phast` for a given node.\r\n\r\nFor resuming :  \r\n- matrix-like calculation : optimal for dense matrix, run *one-to-many*\r\nsearch for `min(length(unique(from)), length(unique(to)))` times,  \r\n- pairwise calculation : optimal for highly sparse matrix, run\r\n*one-to-one* search for `length(from)` times.  \r\n\r\n## User Equilibrium (UE)\r\n\r\nThe term “User Equilibrium” is used to describe a route choice\r\nassumption formally proposed by\r\n[Wardrop](https://doi.org/10.1680/ipeds.1952.11362) :  \r\n**“The journey times on all the routes actually used are equal and less\r\nthan those which would be experienced by a single vehicle on any unused\r\nroutes”**.  \r\nNote that this principle follows directly from the assumptions that\r\ndrivers choose minimum time paths, and are well-informed about network\r\nconditions.\r\n\r\nUnlike AON assignment, this more realistic way to assign flows on a\r\nnetwork take into account **congestion effect**. In this paradigm, the\r\ncost of a given link is dependent of the flow on it.\r\n\r\nAs an example, let’s assume 3000 commuters going from one node to\r\nanother connected by two links. The User equilibrium is illustrated on\r\nthis figure\r\n([source](https://tfresource.org/topics/User_Equilibrium.html)):\r\n\r\n![](https://tfresource.org/topics/UE_Condition.jpg)\r\n\r\nThe relation between cost and flow is called **volume decay function**\r\nand is written as :\r\n\r\n![t = t\\_{0}.(1+\\\\alpha.(\\\\frac{v}{c})^{\\\\beta})](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;t%20%3D%20t_%7B0%7D.%281%2B%5Calpha.%28%5Cfrac%7Bv%7D%7Bc%7D%29%5E%7B%5Cbeta%7D%29 \"t = t_{0}.(1+\\alpha.(\\frac{v}{c})^{\\beta})\")  \r\nwith\r\n![t](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;t \"t\")\r\nthe cost,\r\n![t\\_{0}](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;t_%7B0%7D \"t_{0}\")\r\nthe free-flow cost,\r\n![v](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;v \"v\")\r\nthe flow (or volume),\r\n![c](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;c \"c\")\r\nthe link capacity and\r\n![\\\\alpha, \\\\beta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Calpha%2C%20%5Cbeta \"\\alpha, \\beta\")\r\nunitless parameters.\r\n\r\nTraffic Assignment Problem (TAP) is a convex optimization problem solved\r\nby iterative algorithms. The **relative gap** is the metric to minimize\r\nand is written as :\r\n\r\n![gap=\\|(\\\\frac{TSTT}{SPTT}) - 1\\|](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;gap%3D%7C%28%5Cfrac%7BTSTT%7D%7BSPTT%7D%29%20-%201%7C \"gap=|(\\frac{TSTT}{SPTT}) - 1|\")\r\n\r\nTotal System Travel Time is written as\r\n![TSTT = \\\\sum\\_{x \\\\in E} v\\_{x}.t\\_{x}(v\\_{x})](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;TSTT%20%3D%20%5Csum_%7Bx%20%5Cin%20E%7D%20v_%7Bx%7D.t_%7Bx%7D%28v_%7Bx%7D%29 \"TSTT = \\sum_{x \\in E} v_{x}.t_{x}(v_{x})\")  \r\nShortest Path Travel Time is written as\r\n![SPTT = \\\\sum\\_{x \\\\in E} \\\\hat{v}\\_{x}.t\\_{x}(v\\_{x})](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;SPTT%20%3D%20%5Csum_%7Bx%20%5Cin%20E%7D%20%5Chat%7Bv%7D_%7Bx%7D.t_%7Bx%7D%28v_%7Bx%7D%29 \"SPTT = \\sum_{x \\in E} \\hat{v}_{x}.t_{x}(v_{x})\")  \r\nWith\r\n![E](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;E \"E\")\r\nthe set of edge in the network,\r\n![v](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;v \"v\")\r\nthe volume or flow,\r\n![t](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;t \"t\")\r\nthe cost,\r\n![\\\\hat{v}](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Chat%7Bv%7D \"\\hat{v}\")\r\nthe flow estimated by All-or-Nothing assignment.\r\n\r\n### Link-based algorithms\r\n\r\nThese methods use the descent direction given by AON assignment at each\r\niteration. All links are updated simultaneously using descent direction\r\nand a *step size* parameter\r\n![\\\\theta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Ctheta \"\\theta\").\r\n\r\nLink-based algorithms implemented in `cppRouting` are, in increasing\r\norder of complexity :\r\n\r\n-   **Method of Successive Average** (`msa`) :\r\n    ![\\\\theta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Ctheta \"\\theta\")\r\n    is defined as\r\n    ![\\\\frac{1}{it}](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Cfrac%7B1%7D%7Bit%7D \"\\frac{1}{it}\")\r\n    with\r\n    ![it](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;it \"it\")\r\n    the actual number of iteration.  \r\n-   **Frank-Wolfe** (`fw`) :\r\n    ![\\\\theta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Ctheta \"\\theta\")\r\n    is computed by minimizing the Beckmann function with bisection\r\n    method.  \r\n-   **Conjugate Frank-Wolfe** (`cfw`) :\r\n    ![\\\\theta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Ctheta \"\\theta\")\r\n    is computed by minimizing the Beckmann function with bisection\r\n    method. Descent direction is computed using AON assignment and\r\n    direction at\r\n    ![it-1](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;it-1 \"it-1\").  \r\n-   **Bi-Conjugate Frank-Wolfe** (`bfw`) :\r\n    ![\\\\theta](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;%5Ctheta \"\\theta\")\r\n    is computed by minimizing the Beckmann function with bisection\r\n    method. Descent direction is computed using AON assignment and\r\n    directions at\r\n    ![it-1](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;it-1 \"it-1\")\r\n    and\r\n    ![it-2](https://latex.codecogs.com/png.image?%5Cdpi%7B110%7D\u0026space;%5Cbg_white\u0026space;it-2 \"it-2\").\r\n\r\nBy going down through that list, we slightly increase computing time in\r\neach iteration but we will need less iterations to reach a given gap.  \r\nLet’s equilibrate traffic within our small network, first we need to\r\nconstruct the network with important parameters : `capacity`, `alpha`\r\nand `beta`.\r\n\r\n**For these algorithms, 99% of computation time is done within AON\r\nassignment.**\r\n\r\n``` r\r\nsgr \u003c- makegraph(df = net[,c(\"Init.node\", \"Term.node\", \"Free.Flow.Time\")], \r\n                 directed = TRUE,\r\n                 capacity = net$Capacity,\r\n                 alpha = net$B,\r\n                 beta = net$Power)\r\n\r\n\r\ntraffic \u003c- assign_traffic(Graph = sgr,  from = trips$from, to = trips$to, demand = trips$demand, \r\n                          max_gap = 1e-6, algorithm = \"bfw\", verbose = FALSE)\r\n  \r\ntraffic$gap\r\n```\r\n\r\n    ## [1] 0.0000009230227\r\n\r\nReturned data contains the equilibrated network with the following edge\r\nattributes :  \r\n- `ftt` : free-travel time i.e. the initial cost set during graph\r\nconstruction,  \r\n- `cost` : actual travel time at equilibrium,  \r\n- `flow` : equilibrated flow.\r\n\r\n``` r\r\nhead(traffic$data)\r\n```\r\n\r\n    ##   from to ftt     cost      flow  capacity alpha beta\r\n    ## 1    1  2   6 6.000779  4442.890 25900.201  0.15    4\r\n    ## 2    1  3   4 4.008658  8111.525 23403.473  0.15    4\r\n    ## 3    2  1   6 6.000829  4511.531 25900.201  0.15    4\r\n    ## 4    2  6   5 6.545270  5940.297  4958.181  0.15    4\r\n    ## 5    3  1   4 4.008369  8042.884 23403.473  0.15    4\r\n    ## 6    3  4   4 4.262114 13910.673 17110.524  0.15    4\r\n\r\nNow, we can plot the result with line size varying with road capacity\r\nand the color with flow.\r\n\r\n``` r\r\npar(mfrow=c(1,2),mar=c(3,0,3,0))\r\ng \u003c- graph_from_data_frame(aon[,c(\"from\", \"to\", \"flow\")], directed = TRUE)\r\nc_scale \u003c- colorRamp(c('#60AF66', 'yellow', 'red'))\r\nmaxflow \u003c- max(c(aon$flow, traffic$data$flow))\r\n\r\n#Applying the color scale to edge weights.\r\n#rgb method is to convert colors to a character vector.\r\nE(g)$color = apply(c_scale(aon$flow/maxflow), 1, function(x) rgb(x[1]/255,x[2]/255,x[3]/255) )\r\nset.seed(5)\r\nplot(g, edge.width = traffic$data$capacity/2000, edge.arrow.size = 0, main = \"AON assignment\")\r\nbox(col = \"black\")\r\n\r\n\r\ng \u003c- graph_from_data_frame(traffic$data[,c(\"from\", \"to\", \"flow\")], directed = TRUE)\r\nE(g)$color = apply(c_scale(traffic$data$flow/maxflow), 1, function(x) rgb(x[1]/255,x[2]/255,x[3]/255) )\r\nset.seed(5)\r\nplot(g, edge.width = traffic$data$capacity/2000, edge.arrow.size = 0, main = \"Flows at equilibrium\")\r\nbox(col = \"black\")\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-30-1.png)\u003c!-- --\u003e\r\n\r\n### Bush-based algorithms\r\n\r\n`cppRouting` also propose a bush-based algorithm called **Algorithm B**\r\nfrom [R.B. Dial (2006)](https://doi.org/10.1016/j.trb.2006.02.008).  \r\nThe problem is decomposed into sub-problems, corresponding to each\r\norigin of the OD matrix, that operate on acyclic sub-networks of the\r\noriginal transportation network, called **bushes**. Link flows are\r\nshifted from the longest path to the shortest path recursively within\r\neach bush using Newton method. Unlike link-based algorithm, Algorithm B\r\ncan achieve very precise solution by minimizing relative gap down to\r\n1e-16.\r\n\r\nThe main steps of the procedure are :\r\n\r\n**Initialization**  \r\n- bushes initialization : for each unique origin node of the OD matrix,\r\nan acyclic sub-network is created by computing shortest path tree from\r\nroot node to all other nodes.\r\n\r\nFor each bush :  \r\n- Topological ordering of the nodes  \r\n- for each node, shortest path and longest path is computed and stored.  \r\n- given an OD matrix, flows are loaded on each bush using shortest path labels.  \r\n\r\n**Iteration**  \r\nFor each bush :  \r\n- shortest and longest paths are updated  \r\n- bush optimization : the bush is updated by removing unused links (no\r\nflow on it) and adding links leading to shorter paths. Network costs are\r\nupdated in the same time.  \r\n- bush equilibration : the flow is shifted from the shortest to the\r\nlongest path using Newton method.\r\n\r\n**Evaluation**  \r\nWe compute AON assignment and relative gap.\r\n\r\nFor detailed explanations of Algorithm B, please see this course ([part\r\n1](https://cistup.iisc.ac.in/tarun/CE_272/Lecture_23.pdf), [part\r\n2](https://cistup.iisc.ac.in/tarun/CE_272/Lecture_24.pdf)).\r\n\r\n**Important note : computation time for algorithm-B is depending of the\r\nnumber of origin node AND AON assignment.**\r\n\r\nAlgorithm B is used by setting `dial` to `algorithm` argument :\r\n\r\n``` r\r\ntraffic \u003c- assign_traffic(Graph = sgr,  from = trips$from, to = trips$to, demand = trips$demand, \r\n                          max_gap = 1e-6, algorithm = \"dial\", verbose = TRUE)\r\n```\r\n\r\n    ## Bushes initialization...\r\n    ## Iterating...\r\n    ## iteration 1 : 0.05019\r\n    ## iteration 2 : 0.0152742\r\n    ## iteration 3 : 0.00363109\r\n    ## iteration 4 : 0.000592523\r\n    ## iteration 5 : 0.000106401\r\n    ## iteration 6 : 3.00525e-05\r\n    ## iteration 7 : 3.5758e-05\r\n    ## iteration 8 : 1.21886e-05\r\n    ## iteration 9 : 6.67853e-06\r\n    ## iteration 10 : 1.659e-06\r\n    ## iteration 11 : 2.34866e-07\r\n\r\n``` r\r\ntraffic$gap\r\n```\r\n\r\n    ## [1] 0.000000234866\r\n\r\n### Performance comparison\r\n\r\nNow, let’s measure performance of Traffic assignment algorithms on a\r\nlarger road network. We use Chicago network, composed of 12982 nodes and\r\n39018 edges.\r\n\r\n``` r\r\nnet \u003c- read.csv(\"chicagoregional_net.csv\")\r\nhead(net)\r\n```\r\n\r\n    ##   from    to capacity length ftime    B power speed toll type\r\n    ## 1    1 10293   100000   0.45     0 0.15     4    25    0    3\r\n    ## 2    2 10294   100000   0.50     0 0.15     4    25    0    3\r\n    ## 3    3 10295   100000   0.39     0 0.15     4    25    0    3\r\n    ## 4    4 10296   100000   0.58     0 0.15     4    25    0    3\r\n    ## 5    5 10297   100000   0.50     0 0.15     4    25    0    3\r\n    ## 6    6 10298   100000   0.58     0 0.15     4    25    0    3\r\n\r\nOD matrix contains 2,297,945 trips.\r\n\r\n``` r\r\ntrips \u003c- read.csv(\"chicagoregional_trips.csv\")\r\nhead(trips)\r\n```\r\n\r\n    ##   from to demand\r\n    ## 1    1  1  28.94\r\n    ## 2    1  2  16.58\r\n    ## 3    1  3   2.01\r\n    ## 4    1  4  13.99\r\n    ## 5    1  5   4.90\r\n    ## 6    1  6   5.10\r\n\r\nSince All-or-Nothing algorithm will be called multiple times, we have to\r\nchoose the fastest AON assignment algorithm.\r\n\r\n``` r\r\n# Construct graph with link attributes\r\nsgr \u003c- makegraph(df = net[,c(\"from\", \"to\", \"ftime\")], directed = TRUE,\r\n                 capacity = net$capacity, alpha = net$B, beta = net$power)\r\n\r\n# benchmark using all cores. We don't test NBA* because we don't have node coordinates\r\nRcppParallel::setThreadOptions(numThreads = parallel::detectCores())\r\n\r\nmicrobenchmark(\r\n  pairwise = pw \u003c- get_aon(Graph = sgr, from = trips$from, to = trips$to, demand = trips$demand, algorithm = \"bi\"),\r\n  matrixlike = ml \u003c- get_aon(Graph = sgr, from = trips$from, to = trips$to, demand = trips$demand, algorithm = \"d\"),\r\n  times = 1\r\n)\r\n```\r\n\r\n    ## Unit: seconds\r\n    ##        expr       min        lq      mean    median        uq       max neval\r\n    ##    pairwise 75.414786 75.414786 75.414786 75.414786 75.414786 75.414786     1\r\n    ##  matrixlike  1.411425  1.411425  1.411425  1.411425  1.411425  1.411425     1\r\n\r\nBy setting matrix-like AON calculation, we speed up computation time by\r\na factor of 50 for link-based methods.\r\n\r\n``` r\r\nsgr \u003c- makegraph(df = net[,c(\"from\", \"to\", \"ftime\")], directed = TRUE,\r\n                 capacity = net$capacity, alpha = net$B, beta = net$power)\r\n\r\n# we set 5 minutes for each algorithm (about 600 iterations for link-based, and 20 for algorithm-b)\r\n## capture console output for plotting\r\nouts \u003c- list()\r\ntimes \u003c- list()\r\nfor (i in c(\"msa\", \"fw\", \"cfw\", \"bfw\", \"dial\")){\r\n  max_it \u003c- 600\r\n  if (i == \"dial\"){\r\n    max_it \u003c- 20\r\n  }\r\n  \r\n  stdout \u003c- vector(mode = \"character\")\r\n  con    \u003c- textConnection('stdout', 'wr', local = TRUE)\r\n  sink(con)\r\n  st \u003c- system.time(\r\n  traffic \u003c- assign_traffic(Graph = sgr,  from = trips$from, to = trips$to, demand = trips$demand, \r\n                          max_gap = 1e-6, algorithm = i, aon_method = \"d\", verbose = TRUE, max_it = max_it)\r\n  )\r\n  sink()\r\n  outs[[i]] \u003c- stdout\r\n  times[[i]] \u003c- st\r\n  rm(stdout)\r\n}\r\n\r\n# Plotting gap vs time\r\n\r\ndfp \u003c- list()\r\nfor (i in c(\"msa\", \"fw\", \"cfw\", \"bfw\", \"dial\")){\r\n  out \u003c- outs[[i]]\r\n  out \u003c- out[grepl(\"iteration\", out)]\r\n  out \u003c- sapply(strsplit(out, \" : \"), function(x) as.numeric(x[2]))\r\n  \r\n  df \u003c- data.frame(gap = out, time = 1:length(out)  * (times[[i]][1]/length(out)), algo = i)\r\n  dfp[[i]] \u003c- df\r\n}\r\ndfp \u003c- do.call(rbind, dfp)\r\n\r\noptions(scipen=999)\r\np \u003c- ggplot(dfp, aes(x = time, y = gap, colour = algo))+\r\n  geom_line(size = 1)+\r\n  scale_y_log10()+\r\n  theme_bw()+\r\n  labs(x = \"Time(second)\", y = \"Relative gap (log10 scale)\", colour = \"Algorithm\")+\r\n  scale_color_hue(labels = c(\"Bi-Conjugate FW\", \"Conjugate FW\", \"Algorithm B\", \"Frank-Wole\", \"MSA\"))\r\n  \r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-35-1.png)\u003c!-- --\u003e\r\n\r\nAlgorithm B could achieve very small gap within an reasonable amount of\r\ntime, while link-based methods seems to converge much more slowly.  \r\nFor achieving highly precise solutions, algorithm B should be\r\nimplemented. However, algorithm B generally use more memory than\r\nlink-based methods, depending network size and OD matrix. On the other\r\nhand, link-based method may have a higher benefit through parallel\r\ncomputing since computation time is mainly due to AON assignment.\r\n\r\n**Note : network can be contracted “on-the-fly” at each iteration to\r\nspeed-up AON calculation, by setting `cphast` or `cbi` in `aon_method`\r\nargument.**\r\n\r\n# Algorithm compatibility\r\n\r\nHere is a table summarizing `cppRouting` functions compatibility with\r\nnetwork nature (contracted, simplified, normal).\r\n\r\n\u003ctable class=\"table\" style=\"width: auto !important; margin-left: auto; margin-right: auto;\"\u003e\r\n\u003cthead\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\nFunctions\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\ncontracted\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\nsimplified\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003c/thead\u003e\r\n\u003ctbody\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nassign_traffic\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_aon\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_detour\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_matrix - one weight\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_matrix - two weights\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_pair - one weight\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_pair - two weights\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_isochrone\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_multi_paths\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_path_pair\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tbody\u003e\r\n\u003c/table\u003e\r\n\r\n# Parallel implementation\r\n\r\nNow a table summarizing `cppRouting` functions compatibility with\r\nparallel computing.\r\n\r\n\u003ctable class=\"table\" style=\"width: auto !important; margin-left: auto; margin-right: auto;\"\u003e\r\n\u003cthead\u003e\r\n\u003ctr\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\nFunctions\r\n\u003c/th\u003e\r\n\u003cth style=\"text-align:left;\"\u003e\r\nmultithreaded\r\n\u003c/th\u003e\r\n\u003c/tr\u003e\r\n\u003c/thead\u003e\r\n\u003ctbody\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nmakegraph\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\ncpp_contract\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\ncpp_simplify\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\nno\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nassign_traffic - link-based\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nassign_traffic - bush-based\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: red !important;\"\u003e\r\npartly\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_aon\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_detour\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_matrix\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_distance_pair\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_isochrone\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_multi_paths\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003ctr\u003e\r\n\u003ctd style=\"text-align:left;font-weight: bold;\"\u003e\r\nget_path_pair\r\n\u003c/td\u003e\r\n\u003ctd style=\"text-align:left;background-color: green !important;\"\u003e\r\nyes\r\n\u003c/td\u003e\r\n\u003c/tr\u003e\r\n\u003c/tbody\u003e\r\n\u003c/table\u003e\r\n\r\n# Applications\r\n\r\n**Except application 4, all indicators are calculated at the country\r\nscale but for the limited `R`’s ability to plot large shapefile, only\r\none region is mapped.**\r\n\r\n## Application 1 : Calculate Two Step Floating Catchment Areas (2SFCA) of general practitioners in France\r\n\r\n2SFCA method is explained here :\r\n\u003chttps://en.wikipedia.org/wiki/Two-step_floating_catchment_area_method\u003e\r\n\r\n**First step**  \r\nIsochrones are calculated with the `cppRouting` function `get_isochrone`\r\n\r\n``` r\r\n#Isochrone around doctor locations with time limit of 15 minutes\r\niso\u003c-get_isochrone(graph,\r\n                   from = ndcom[ndcom$com %in% med$CODGEO,\"id_noeud\"],\r\n                   lim = 15,\r\n                   keep=ndcom$id_noeud, #return only commune nodes\r\n                   long = TRUE) #data.frame output\r\n#Joining and summing population located in each isochrone\r\niso\u003c-left_join(iso,ndcom[,c(\"id_noeud\",\"POPULATION\")],by=c(\"node\"=\"id_noeud\"))\r\ndf\u003c-iso %\u003e% group_by(origin) %\u003e%\r\n  summarise(pop=sum(POPULATION))\r\n#Joining number of doctors \r\ndf\u003c-left_join(df,med[,c(\"id_noeud\",\"NB_D201\")],by=c(\"origin\"=\"id_noeud\"))\r\n#Calculate ratios\r\ndf$ratio\u003c-df$NB_D201/df$pop\r\n```\r\n\r\n**Second step**\r\n\r\n``` r\r\n#Isochrone around each commune with time limit of 15 minutes (few seconds to compute)\r\niso2\u003c-get_isochrone(graph,\r\n                    from=ndcom$id_noeud,\r\n                    lim = 15,\r\n                    keep=ndcom$id_noeud,\r\n                    long=TRUE)\r\n#Joining and summing ratios calculated in first step\r\ndf2\u003c-left_join(iso2,df[,c(\"origin\",\"ratio\")],by=c(\"node\"=\"origin\"))\r\ndf2\u003c-df2 %\u003e% group_by(origin) %\u003e%\r\n  summarise(sfca=sum(ratio,na.rm=T))\r\n```\r\n\r\n**Plot the map for Bourgogne-Franche-Comte region**\r\n\r\n``` r\r\n#Joining commune IDs to nodes\r\ndf2\u003c-left_join(df2,ndcom[,c(\"id_noeud\",\"com\")],by=c(\"origin\"=\"id_noeud\"))\r\n#Joining 2SFCA to shapefile\r\ncom\u003c-left_join(com,df2[,c(\"com\",\"sfca\")],by=c(\"INSEE_COM\"=\"com\"))\r\n#Plot for one region\r\np\u003c-tm_shape(com[com$NOM_REG==\"BOURGOGNE-FRANCHE-COMTE\",]) + \r\n  tm_fill(col = \"sfca\",style=\"cont\")+\r\n  tm_layout(main.title=\"2SFCA applied to general practitioners\",legend.outside=TRUE)\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-40-1.png)\u003c!-- --\u003e\r\n\r\n## Application 2 : Calculate the minimum travel time to the closest maternity ward in France\r\n\r\n**Shortest travel time matrix**  \r\nThe shortest travel time is computed with the `cppRouting` function\r\n`get_distance_matrix`. In order to compute multiple distances from one\r\nsource, original uni-directional Dijkstra algorithm is ran without early\r\nstopping.  \r\nWe compute travel time from all commune nodes to all maternity ward\r\nnodes (i.e \\~36000\\*400 distances).\r\n\r\n``` r\r\n#Distance matrix on contracted graph\r\ndists\u003c-get_distance_matrix(graph3,\r\n                           from=ndcom$id_noeud,\r\n                           to=ndcom$id_noeud[ndcom$com %in% maternity$CODGEO],\r\n                           algorithm = \"phast\")#because of the rectangular shape of the matrix\r\n#We extract each minimum travel time for all the communes\r\ndists2\u003c-data.frame(node=ndcom$id_noeud,mindist=apply(dists,1,min,na.rm=T))\r\n#Joining commune IDs to nodes\r\ndists2\u003c-left_join(dists2,ndcom[,c(\"id_noeud\",\"com\")],by=c(\"node\"=\"id_noeud\"))\r\n#Joining minimum travel time to the shapefile\r\ncom\u003c-left_join(com,dists2[,c(\"com\",\"mindist\")],by=c(\"INSEE_COM\"=\"com\"))\r\n```\r\n\r\n**Plot the map of minimum travel time in Bourgogne-Franche-Comte\r\nregion**\r\n\r\n``` r\r\np\u003c-tm_shape(com[com$NOM_REG==\"BOURGOGNE-FRANCHE-COMTE\",]) + \r\n  tm_fill(col = \"mindist\",style=\"cont\",palette=\"Greens\",title=\"Minutes\")+\r\n  tm_layout(main.title=\"Travel time to the closest maternity ward\",legend.outside=T)\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-42-1.png)\u003c!-- --\u003e\r\n\r\n## Application 3 : Calculate average commuting time to go to job in France\r\n\r\nCommuting data from national census is composed of 968794 unique pairs\r\nof origin - destination locations (home municipality, job municipality).\r\nUsing other `R` packages like `igraph` or `dodgr`, we would have to\r\ncalculate the whole matrix between all communes (36000 x 36000). We\r\nwould end with a 10Gb matrix whereas we only need 0.075% of the result\r\n(968794/(36000 x 36000)).  \r\nSo this example illustrate the advantage of calculating distance by\r\n*pair*.\r\n\r\n``` r\r\n#Merge node to communes\r\nndcom$id_noeud\u003c-as.character(ndcom$id_noeud)\r\ncmt$node1\u003c-ndcom$id_noeud[match(cmt$CODGEO,ndcom$com)]\r\ncmt$node2\u003c-ndcom$id_noeud[match(cmt$DCLT,ndcom$com)]\r\ncmt\u003c-cmt[!is.na(cmt$node1) \u0026 !is.na(cmt$node2),]\r\n\r\n#Calculate distance for each pair using contracted graph\r\ndist\u003c-get_distance_pair(graph3,from=cmt$node1,to=cmt$node2)\r\n\r\n#Mean weighted by the flow of each pair\r\ntraveltime\u003c-cbind(cmt,dist)\r\ntraveltime\u003c-traveltime %\u003e% group_by(CODGEO) %\u003e% summarise(time=weighted.mean(dist,flux,na.rm=T))\r\n```\r\n\r\n**Plot the map of average travel time of Bourgogne-Franche-Comte\r\ninhabitants**\r\n\r\n``` r\r\n#Merge to shapefile\r\ncom\u003c-left_join(com,traveltime,by=c(\"INSEE_COM\"=\"CODGEO\"))\r\n\r\np\u003c-tm_shape(com[com$NOM_REG==\"BOURGOGNE-FRANCHE-COMTE\",]) +\r\n  tm_fill(col = \"time\",style=\"quantile\",n=5,title=\"Minutes\",palette=\"Reds\")+\r\n  tm_layout(main.title=\"Average travel time\",legend.outside=TRUE)\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-44-1.png)\u003c!-- --\u003e\r\n\r\n## Application 4 : Calculate the flow of people crossing each municipality in the context of commuting in Bourgogne-Franche-Comte region\r\n\r\nFirst, we must determine in which commune each node is located.\r\n\r\n``` r\r\n#Convert nodes coordinates to spatial points\r\npts\u003c-st_as_sf(coord,coords = c(\"X\",\"Y\"),crs=2154)\r\nst_crs(com)\u003c-st_crs(pts)\r\n```\r\n\r\n    ## Warning: st_crs\u003c- : replacing crs does not reproject data; use st_transform for\r\n    ## that\r\n\r\n``` r\r\n#Spatial join commune ID to each node\r\nspjoin\u003c-st_join(pts,com[,\"INSEE_COM\"])\r\nst_geometry(spjoin)\u003c-NULL\r\nspjoin$ID\u003c-as.character(spjoin$ID)\r\n#Vector of commune IDs of Bourgogne-Franche-Comte region\r\ncomBourg\u003c-com$INSEE_COM[com$NOM_REG==\"BOURGOGNE-FRANCHE-COMTE\"]\r\n#Calculate shortest paths for all commuters on the contracted graph\r\nshortPath\u003c-get_path_pair(graph3,\r\n                         from=cmt$node1,\r\n                         to=cmt$node2,\r\n                         keep = spjoin$ID[spjoin$INSEE_COM %in% comBourg],#return only the nodes in Bourgogne Franche-Comte because of memory usage\r\n                         long = TRUE)#return a long data.frame instead of a list\r\n\r\n#Joining commune ID to each traveled node\r\nshortPath\u003c-left_join(shortPath,spjoin,by=c(\"node\"=\"ID\"))\r\n```\r\n\r\nIf a commuter crosses multiple nodes in a municipality, we count it only\r\nonce.\r\n\r\n``` r\r\n#Remove duplicated \r\nshortPath\u003c-shortPath[!duplicated(shortPath[,c(\"from\",\"to\",\"INSEE_COM\")]),]\r\n\r\n#Joining flow per each commuter\r\nshortPath\u003c-left_join(shortPath,\r\n                     cmt[,c(\"node1\",\"node2\",\"flux\")],\r\n                     by=c(\"from\"=\"node1\",\"to\"=\"node2\"))\r\n\r\n#Aggregating flows by each commune\r\nshortPath\u003c-shortPath %\u003e% group_by(INSEE_COM) %\u003e% summarise(flow=sum(flux))\r\n```\r\n\r\n**Plot the flow of people crossing Bourgogne-Franche-Comte’s communes **\r\n\r\n``` r\r\n#Merge to shapefile\r\ncom\u003c-left_join(com,shortPath,by=\"INSEE_COM\")\r\n\r\np\u003c-tm_shape(com[com$NOM_REG==\"BOURGOGNE-FRANCHE-COMTE\",]) + \r\n  tm_fill(col = \"flow\",style=\"quantile\",n=10,palette=\"Blues\")+\r\n  tm_layout(main.title=\"Commuters flow\",legend.outside=TRUE)\r\np\r\n```\r\n\r\n![](README_files/figure-gfm/unnamed-chunk-47-1.png)\u003c!-- --\u003e\r\n\r\n# Benchmark with other R packages\r\n\r\nTo show the efficiency of `cppRouting`, we can make some benchmarking\r\nwith the famous R package `igraph`, and the `dodgr` package. Estimation\r\nof the User Equilibrium cannot be compared because of the absence of\r\nthis kind of algorithm within `R` landscape (to my knowledge).\r\n\r\n**Distance matrix : one core**\r\n\r\n``` r\r\nlibrary(igraph)\r\nlibrary(dodgr)\r\n#Sampling 1000 random origin/destination nodes (1000000 distances to compute)\r\nset.seed(10)\r\norigin\u003c-sample(unique(roads$from),1000,replace = F)\r\ndestination\u003c-sample(unique(roads$from),1000,replace = F)\r\n```\r\n\r\n``` r\r\nRcppParallel::setThreadOptions(1)\r\n#igraph graph\r\ngraph_igraph\u003c-graph_from_data_frame(roads,directed = TRUE)\r\n\r\n#dodgr graph\r\n\r\nroads2\u003c-roads\r\ncolnames(roads2)[3]\u003c-\"dist\"\r\n\r\n#benchmark\r\nmicrobenchmark(igraph=test_igraph\u003c-distances(graph_igraph,origin,to=destination,weights = E(graph_igraph)$weight,mode=\"out\"),\r\n               dodgr=test_dodgr\u003c-dodgr_dists(graph=roads2,from=origin,to=destination),\r\n               cpprouting=test_cpp\u003c-get_distance_matrix(graph,origin,destination),\r\n               contr_mch=test_mch\u003c-get_distance_matrix(graph3,origin,destination,algorithm = \"mch\"),\r\n               contr_phast=test_phast\u003c-get_distance_matrix(graph3,origin,destination,algorithm = \"phast\"),\r\n               times=1,\r\n               unit = 's')\r\n```\r\n\r\n    ## Unit: seconds\r\n    ##         expr       min        lq      mean    median        uq       max neval\r\n    ##       igraph 64.924762 64.924762 64.924762 64.924762 64.924762 64.924762     1\r\n    ##        dodgr 79.129622 79.129622 79.129622 79.129622 79.129622 79.129622     1\r\n    ##   cpprouting 28.453509 28.453509 28.453509 28.453509 28.453509 28.453509     1\r\n    ##    contr_mch  0.637912  0.637912  0.637912  0.637912  0.637912  0.637912     1\r\n    ##  contr_phast  4.360011  4.360011  4.360011  4.360011  4.360011  4.360011     1\r\n\r\nEven if we add the preprocessing time (i.e. 14s) to the query time, the\r\nwhole process of contraction hierarchies is still faster.\r\n\r\n**Ouput**\r\n\r\n``` r\r\nhead(cbind(test_igraph[,1],test_dodgr[,1],test_cpp[,1],test_mch[,1],test_phast[,1]))\r\n```\r\n\r\n    ##            [,1]     [,2]     [,3]     [,4]     [,5]\r\n    ## 151604 442.6753 442.6753 442.6753 442.6753 442.6753\r\n    ## 111317 415.5493 415.6265 415.5493 415.5493 415.5493\r\n    ## 212122 344.0450 344.0450 344.0450 344.0450 344.0450\r\n    ## 215162 340.6700 340.6700 340.6700 340.6700 340.6700\r\n    ## 159720 385.1082 385.1082 385.1082 385.1082 385.1082\r\n    ## 168762 513.4273 513.5044 513.4273 513.4273 513.4273\r\n\r\n**All-or-Nothing assignment : sparse OD matrix**\r\n\r\n``` r\r\n#generate random flows\r\nflows \u003c- sample(10:1000, 1000, replace = TRUE)\r\nflows2 \u003c- diag(flows, nrow = 1000, ncol = 1000)\r\n\r\nmicrobenchmark(dodgr=test_dodgr\u003c-dodgr_flows_aggregate(graph = roads2, from = origin, to = destination, flows = flows2, contract = FALSE, norm_sums = FALSE, tol = 0),\r\n               cpprouting=test_cpp \u003c- get_aon(graph, from = origin, to = destination, demand = flows, algorithm = \"bi\"),\r\n               cpprouting_ch=test_ch \u003c- get_aon(graph3, from = origin, to = destination, demand = flows, algorithm = \"bi\"),\r\n               times=1,\r\n               unit = 's')\r\n```\r\n\r\n    ## Unit: seconds\r\n    ##           expr        min         lq       mean     median         uq\r\n    ##          dodgr 39.9192510 39.9192510 39.9192510 39.9192510 39.9192510\r\n    ##     cpprouting 11.6453517 11.6453517 11.6453517 11.6453517 11.6453517\r\n    ##  cpprouting_ch  0.6071558  0.6071558  0.6071558  0.6071558  0.6071558\r\n    ##         max neval\r\n    ##  39.9192510     1\r\n    ##  11.6453517     1\r\n    ##   0.6071558     1\r\n\r\n**All-or-Nothing assignment : dense OD matrix**\r\n\r\n``` r\r\n#generate random flows\r\nflows \u003c- sample(10:1000, 1000000, replace = TRUE)\r\nflows2 \u003c- matrix(flows, nrow = 1000, ncol = 1000)\r\ndf_flows \u003c- as.data.frame(flows2)\r\ncolnames(df_flows) \u003c- destination\r\ndf_flows$from \u003c- origin\r\ndf_flows \u003c- reshape2::melt(df_flows, id.vars = \"from\", variable.name = \"to\")\r\n\r\nmicrobenchmark(dodgr=test_dodgr\u003c-dodgr_flows_aggregate(graph = roads2, from = origin, to = destination, flows = flows2, contract = FALSE, norm_sums = FALSE, tol = 0),\r\n               cpprouting=test_cpp \u003c- get_aon(graph, from = df_flows$from, to = df_flows$to, demand = df_flows$value, algorithm = \"d\"),\r\n               cpprouting_ch=test_ch \u003c- get_aon(graph3, from = df_flows$from, to = df_flows$to, demand = df_flows$value, algorithm = \"phast\"),\r\n               times=1,\r\n               unit = 's')\r\n```\r\n\r\n    ## Unit: seconds\r\n    ##           expr      min       lq     mean   median       uq      max neval\r\n    ##          dodgr 98.68688 98.68688 98.68688 98.68688 98.68688 98.68688     1\r\n    ##     cpprouting 31.16458 31.16458 31.16458 31.16458 31.16458 31.16458     1\r\n    ##  cpprouting_ch  5.91544  5.91544  5.91544  5.91544  5.91544  5.91544     1\r\n\r\n# Citation\r\n\r\nPlease don’t forget to cite `cppRouting` package in your work !\r\n\r\n``` r\r\ncitation(\"cppRouting\")\r\n```\r\n","funding_links":["https://www.paypal.com/donate/?hosted_button_id=SNBP8ZWMMYCDA"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvlarmet%2Fcpprouting","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvlarmet%2Fcpprouting","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvlarmet%2Fcpprouting/lists"}