Distances on Directed Graphs in R
at main 454 lines 17 kB view raw
1#include "graph.h" 2 3//' get_to_from 4//' 5//' Get one pair of two and from edges and vertices. Main task is to make sure 6//' that bi-directed edges ("intermediate_double") correctly return the 7//' **different** values of from and to vertices and edges. 8//' 9//' @noRd 10void graph_contract::get_to_from (const edge_map_t &edge_map, 11 const std::unordered_set <edge_id_t> &edges, 12 const std::vector <vertex_id_t> &two_nbs, 13 vertex_id_t &vt_from, vertex_id_t &vt_to, 14 edge_id_t &edge_from_id, edge_id_t &edge_to_id) 15{ 16 for (auto edge_id: edges) 17 { 18 edge_t edge = edge_map.find (edge_id)->second; 19 if (edge_from_id == "") 20 { 21 if (edge.get_from_vertex () == two_nbs [0] && 22 (edge_to_id == "" || vt_to != two_nbs [0])) 23 { 24 edge_from_id = edge_id; 25 vt_from = two_nbs [0]; 26 } else if (edge.get_from_vertex () == two_nbs [1] && 27 (edge_to_id == "" || vt_to != two_nbs [1])) 28 { 29 edge_from_id = edge_id; 30 vt_from = two_nbs [1]; 31 } 32 } 33 if (edge_to_id == "") 34 { 35 if (edge.get_to_vertex () == two_nbs [0] && 36 (edge_from_id == "" || vt_from != two_nbs [0])) 37 { 38 edge_to_id = edge_id; 39 vt_to = two_nbs [0]; 40 } else if (edge.get_to_vertex () == two_nbs [1] && 41 (edge_from_id == "" || vt_from != two_nbs [1])) 42 { 43 edge_to_id = edge_id; 44 vt_to = two_nbs [1]; 45 } 46 } 47 } 48} 49 50void graph_contract::contract_one_edge (vert2edge_map_t &vert2edge_map, 51 vertex_map_t &vertex_map, edge_map_t &edge_map, 52 const std::unordered_set <edge_id_t> &edgelist, 53 const vertex_id_t vtx_id, const vertex_id_t vt_from, 54 const vertex_id_t vt_to, 55 const edge_id_t edge_from_id, const edge_id_t edge_to_id, 56 const edge_id_t new_edge_id, 57 bool has_times) 58{ 59 edge_t edge_from = edge_map.find (edge_from_id)->second, 60 edge_to = edge_map.find (edge_to_id)->second; 61 double time = -INFINITE_DOUBLE, timew = -INFINITE_DOUBLE, 62 d = edge_from.dist + edge_to.dist, 63 w = edge_from.weight + edge_to.weight; 64 if (has_times) 65 { 66 time = edge_from.time + edge_to.time; 67 timew = edge_from.timew + edge_to.timew; 68 } 69 70 std::vector <edge_id_t> old_edges, 71 old_edges_fr = edge_from.get_old_edges (), 72 old_edges_to = edge_to.get_old_edges (); 73 for (auto e: old_edges_fr) 74 old_edges.push_back (e); 75 for (auto e: old_edges_to) 76 old_edges.push_back (e); 77 // Only insert edge IDs that are in the original list 78 if (edgelist.find (edge_from_id) != edgelist.end ()) 79 old_edges.push_back (edge_from_id); 80 if (edgelist.find (edge_to_id) != edgelist.end ()) 81 old_edges.push_back (edge_to_id); 82 83 graph::erase_from_v2e_map (vert2edge_map, vtx_id, edge_from_id); 84 graph::erase_from_v2e_map (vert2edge_map, vtx_id, edge_to_id); 85 graph::erase_from_v2e_map (vert2edge_map, vt_from, edge_from_id); 86 graph::erase_from_v2e_map (vert2edge_map, vt_to, edge_to_id); 87 graph::add_to_v2e_map (vert2edge_map, vt_from, new_edge_id); 88 graph::add_to_v2e_map (vert2edge_map, vt_to, new_edge_id); 89 90 vertex_t vt = vertex_map [vt_from]; 91 vt.replace_neighbour (vtx_id, vt_from); 92 vertex_map [vt_from] = vt; 93 vt = vertex_map [vt_to]; 94 vt.replace_neighbour (vtx_id, vt_to); 95 vertex_map [vt_to] = vt; 96 97 edge_map.erase (edge_from_id); 98 edge_map.erase (edge_to_id); 99 std::vector <double> wts; 100 if (!has_times) 101 wts = {d, w}; 102 else 103 wts = {d, w, time, timew}; 104 edge_t new_edge = edge_t (vt_from, vt_to, wts, new_edge_id, old_edges); 105 edge_map.emplace (new_edge_id, new_edge); 106} 107 108//' same_hwy_type 109//' 110//' Determine whether two edges represent the same weight category (type of 111//' highway for street networks, for example). Categories are not retained in 112//' converted graphs, but can be discerned by comparing ratios of weighted to 113//' non-weighted distances. 114//' @noRd 115bool graph_contract::same_hwy_type (const edge_map_t &edge_map, 116 const edge_id_t &e1, const edge_id_t &e2) 117{ 118 const double tol = 1.0e-6; 119 120 edge_t edge1 = edge_map.find (e1)->second, 121 edge2 = edge_map.find (e2)->second; 122 123 return (fabs (edge1.weight / edge1.dist - edge2.weight / edge2.dist) < tol); 124} 125 126 127// See docs/graph-contraction for explanation of the following code and 128// associated vertex and edge maps. 129void graph_contract::contract_graph (vertex_map_t &vertex_map, 130 edge_map_t &edge_map, vert2edge_map_t &vert2edge_map, 131 std::unordered_set <vertex_id_t> verts_to_keep, 132 bool has_times) 133{ 134 std::unordered_set <vertex_id_t> verts; 135 for (auto v: vertex_map) 136 { 137 if (verts_to_keep.find (v.first) == verts_to_keep.end ()) 138 verts.insert (v.first); 139 } 140 std::unordered_set <edge_id_t> edgelist; 141 for (auto e: edge_map) 142 edgelist.insert (e.first); 143 144 std::vector <edge_id_t> new_edge_ids; 145 146 unsigned int maxid = 123456; 147 148 while (verts.size () > 0) 149 { 150 Rcpp::checkUserInterrupt (); 151 std::unordered_set <vertex_id_t>::iterator vid = verts.begin (); 152 vertex_id_t vtx_id = vertex_map.find (*vid)->first; 153 vertex_t vtx = vertex_map.find (*vid)->second; 154 std::unordered_set <edge_id_t> edges = vert2edge_map [vtx_id]; 155 156 new_edge_ids.clear (); 157 new_edge_ids.push_back ("a" + std::to_string (maxid++)); 158 159 if ((vtx.is_intermediate_single () || vtx.is_intermediate_double ()) && 160 (edges.size () == 2 || edges.size () == 4)) 161 { 162 if (edges.size () == 4) // is_intermediate_double as well! 163 new_edge_ids.push_back ("a" + std::to_string (maxid++)); 164 165 // Get the two adjacent vertices 166 std::unordered_set <vertex_id_t> nbs = vtx.get_all_neighbours (); 167 std::vector <vertex_id_t> two_nbs; 168 two_nbs.reserve (2); 169 for (vertex_id_t nb: nbs) 170 two_nbs.push_back (nb); // size is always 2 171 172 // ensure two edges are same highway type 173 bool hwys_are_same = true; 174 for (edge_id_t new_edge_id: new_edge_ids) 175 { 176 vertex_id_t vt_from = "", vt_to = ""; 177 edge_id_t edge_from_id = "", edge_to_id = ""; 178 graph_contract::get_to_from (edge_map, edges, two_nbs, 179 vt_from, vt_to, edge_from_id, edge_to_id); 180 hwys_are_same = graph_contract::same_hwy_type (edge_map, 181 edge_from_id, edge_to_id); 182 if (!hwys_are_same) 183 break; 184 } 185 186 if (hwys_are_same) 187 { 188 // remove intervening vertex: 189 vertex_t vt0 = vertex_map [two_nbs [0]]; 190 vertex_t vt1 = vertex_map [two_nbs [1]]; 191 // Note that replace neighbour includes bi-directional 192 // replacement, so this works for intermediate_double() too 193 vt0.replace_neighbour (vtx_id, two_nbs [1]); 194 vt1.replace_neighbour (vtx_id, two_nbs [0]); 195 vertex_map [two_nbs [0]] = vt0; 196 vertex_map [two_nbs [1]] = vt1; 197 198 // construct new edge(s) and remove old ones. There are 2 199 // new_edge_ids only for intermediate double vertices 200 // (that is, bi-directional). 201 for (edge_id_t new_edge_id: new_edge_ids) 202 { 203 vertex_id_t vt_from = "", vt_to = ""; 204 edge_id_t edge_from_id = "", edge_to_id = ""; 205 206 // get the from and to edges and vertices 207 graph_contract::get_to_from (edge_map, edges, two_nbs, 208 vt_from, vt_to, edge_from_id, edge_to_id); 209 210 edges.erase (edge_from_id); 211 edges.erase (edge_to_id); 212 // It is possible to have repeated values of two_nbs; 213 // calling these a second time should then just lead to no 214 // contraction 215 if ((vt_from == "") || (vt_to == "")) 216 continue; 217 218 graph_contract::contract_one_edge (vert2edge_map, 219 vertex_map, edge_map, edgelist, vtx_id, vt_from, 220 vt_to, edge_from_id, edge_to_id, new_edge_id, 221 has_times); 222 } 223 } 224 } 225 verts.erase (vtx_id); 226 } 227} 228 229// Sort a vector of edges according to original edge sequence 230void graph_contract::sort_edges ( 231 const std::vector <edge_id_t> &old_edges, 232 const std::unordered_map <edge_id_t, size_t> &edge_sequence, 233 std::vector <edge_id_t> &edges_sorted) { 234 235 std::vector <size_t> old_edge_index (old_edges.size ()); 236 std::set <size_t> index_tmp; 237 for (size_t i = 0; i < old_edges.size (); i++) 238 { 239 old_edge_index [i] = edge_sequence.find (old_edges [i])->second; 240 index_tmp.emplace (old_edge_index [i]); 241 } 242 243 // Convert ordered index_tmp into sorted vector index: 244 std::vector <size_t> index (old_edges.size ()); 245 for (size_t i = 0; i < old_edges.size (); i++) { 246 index [i] = static_cast <size_t> (std::distance (index_tmp.begin (), 247 index_tmp.find (old_edge_index [i]))); 248 } 249 250 // Finally use that index to re-order old_edges: 251 for (size_t i = 0; i < old_edges.size (); i++) { 252 edges_sorted [index [i] ] = old_edges [i]; 253 } 254} 255 256 257//' rcpp_contract_graph 258//' 259//' Removes nodes and edges from a graph that are not needed for routing 260//' 261//' @param graph graph to be processed 262//' 263//' @return \code{Rcpp::List} containing one \code{data.frame} with the 264//' contracted graph, one \code{data.frame} with the original graph and one 265//' \code{data.frame} containing information about the relating edge ids of the 266//' original and contracted graph. 267//' 268//' @noRd 269// [[Rcpp::export]] 270Rcpp::List rcpp_contract_graph (const Rcpp::DataFrame &graph, 271 Rcpp::Nullable <Rcpp::StringVector> &vertlist_in) 272{ 273 std::unordered_set <vertex_id_t> verts_to_keep; 274 if (vertlist_in.isNotNull ()) 275 { 276 Rcpp::StringVector vertlist (vertlist_in); 277 for (R_xlen_t i = 0; i < vertlist.length (); i ++) 278 verts_to_keep.emplace (std::string (vertlist [i])); 279 } 280 281 // Get set of all original edge IDs, as well as a map of original edge 282 // sequence numbers to ensure contracted edges follow original sequences 283 // (used below to fill the final edge_map). 284 std::unordered_set <edge_id_t> original_edges; 285 Rcpp::StringVector edge_id = graph ["edge_id"]; 286 std::unordered_map <edge_id_t, size_t> edge_sequence; 287 size_t i = 0; 288 for (auto e: edge_id) 289 { 290 original_edges.emplace (e); 291 edge_sequence.emplace (e, i++); 292 } 293 294 vertex_map_t vertices; 295 edge_map_t edge_map; 296 vert2edge_map_t vert2edge_map; 297 298 bool has_times = graph::graph_from_df (graph, vertices, edge_map, vert2edge_map); 299 300 vertex_map_t vertices_contracted = vertices; 301 edge_map_t edge_map_contracted = edge_map; 302 303 graph_contract::contract_graph (vertices_contracted, edge_map_contracted, 304 vert2edge_map, verts_to_keep, has_times); 305 306 size_t nedges = edge_map_contracted.size (); 307 308 // These vectors are all for the contracted graph: 309 Rcpp::StringVector from_vec (nedges), to_vec (nedges), 310 edgeid_vec (nedges); 311 Rcpp::NumericVector dist_vec (nedges), weight_vec (nedges), 312 time_vec (nedges), timew_vec (nedges); 313 314 size_t map_size = 0; // size of edge map contracted -> original 315 size_t edge_count = 0; 316 for (auto e = edge_map_contracted.begin (); 317 e != edge_map_contracted.end (); ++e) 318 { 319 vertex_id_t from = e->second.get_from_vertex (); 320 vertex_id_t to = e->second.get_to_vertex (); 321 vertex_t from_vtx = vertices_contracted.at (from); 322 vertex_t to_vtx = vertices_contracted.at (to); 323 324 from_vec (edge_count) = from; 325 to_vec (edge_count) = to; 326 dist_vec (edge_count) = e->second.dist; 327 weight_vec (edge_count) = e->second.weight; 328 if (has_times) 329 { 330 time_vec (edge_count) = e->second.time; 331 timew_vec (edge_count) = e->second.timew; 332 } 333 334 edgeid_vec (edge_count) = e->second.getID (); 335 336 edge_count++; 337 338 if (original_edges.find (e->first) == original_edges.end ()) 339 map_size += e->second.get_old_edges ().size (); 340 } 341 342 // populate the new -> old edge map 343 // The graph_contract::contract_one_edge function unavoidably inserts edges 344 // in random order. They have to be re-ordered here to the order in the 345 // original graph so that uncontracted edges follow original sequences 346 // (#173). 347 348 std::vector <edge_id_t> edge_map_new (map_size), edge_map_old (map_size); 349 size_t count = 0; 350 for (auto e = edge_map_contracted.begin (); 351 e != edge_map_contracted.end (); ++e) 352 { 353 if (original_edges.find (e->first) == original_edges.end ()) 354 { 355 std::vector <edge_id_t> old_edges = e->second.get_old_edges (); 356 std::vector <edge_id_t> edges_sorted (old_edges.size ()); 357 358 if (old_edges.size () == 1L) 359 { 360 edges_sorted [0] = old_edges [0]; 361 } else 362 { 363 graph_contract::sort_edges (old_edges, edge_sequence, edges_sorted); 364 } 365 366 for (auto oe: edges_sorted) 367 { 368 edge_map_new [count] = e->first; 369 edge_map_old [count++] = oe; 370 } 371 } 372 } 373 374 Rcpp::DataFrame contracted; 375 if (!has_times) 376 contracted = Rcpp::DataFrame::create ( 377 Rcpp::Named ("edge_id") = edgeid_vec, 378 Rcpp::Named ("from") = from_vec, 379 Rcpp::Named ("to") = to_vec, 380 Rcpp::Named ("d") = dist_vec, 381 Rcpp::Named ("d_weighted") = weight_vec, 382 Rcpp::_["stringsAsFactors"] = false); 383 else 384 contracted = Rcpp::DataFrame::create ( 385 Rcpp::Named ("edge_id") = edgeid_vec, 386 Rcpp::Named ("from") = from_vec, 387 Rcpp::Named ("to") = to_vec, 388 Rcpp::Named ("d") = dist_vec, 389 Rcpp::Named ("d_weighted") = weight_vec, 390 Rcpp::Named ("time") = time_vec, 391 Rcpp::Named ("timew") = timew_vec, 392 Rcpp::_["stringsAsFactors"] = false); 393 394 Rcpp::DataFrame edges_new2old = Rcpp::DataFrame::create ( 395 Rcpp::Named ("edge_new") = edge_map_new, 396 Rcpp::Named ("edge_old") = edge_map_old, 397 Rcpp::_["stringsAsFactors"] = false); 398 399 return Rcpp::List::create ( 400 Rcpp::Named ("graph") = contracted, 401 Rcpp::Named ("edge_map") = edges_new2old); 402} 403 404//' rcpp_merge_cols 405//' 406//' Merge columns in directed graph to form aggregate undirected columns, and 407//' return a corresponding undirected graph useful for visualisation. 408//' 409//' @param graph The result of a call to \code{dodgr_flows_aggregate/disperse} 410//' or similar function resuling in columns of directed values. 411//' @return A single vector of aggregate values with non-zero values only for 412//' those edges to be retained in the directed graph. 413//' 414//' @noRd 415// [[Rcpp::export]] 416Rcpp::NumericVector rcpp_merge_cols (Rcpp::DataFrame graph) 417{ 418 std::vector <std::string> from = graph ["from"]; 419 std::vector <std::string> to = graph ["to"]; 420 std::vector <double> dist = graph ["d"]; 421 std::vector <double> wt = graph ["d_weighted"]; 422 std::vector <double> aggr_var = graph ["merge"]; // always called "merge" 423 424 // vertvert_map just holds index of where pair of vertices were first found. 425 // These can only be duplicated once, so only one single value is ever 426 // needed. 427 std::unordered_map <std::string, int> vertvert_map; 428 Rcpp::NumericVector aggr_total (from.size ()); 429 for (size_t i = 0; i < from.size (); i++) 430 { 431 R_xlen_t i_r = static_cast <R_xlen_t> (i); 432 std::string ft = "a" + from [i] + "b" + to [i], 433 tf = "a" + to [i] + "b" + from [i]; 434 if (vertvert_map.find (ft) == vertvert_map.end () && 435 vertvert_map.find (tf) == vertvert_map.end ()) 436 { 437 vertvert_map.emplace (ft, i); 438 aggr_total [i_r] = aggr_var [i]; 439 } else 440 { 441 long int where = INFINITE_INT; 442 if (vertvert_map.find (ft) != vertvert_map.end ()) 443 where = vertvert_map.at (ft); 444 else if (vertvert_map.find (tf) != vertvert_map.end ()) 445 where = vertvert_map.at (tf); 446 if (where == INFINITE_INT) 447 Rcpp::stop ("there is no where; this can never happen"); // # nocov 448 449 aggr_total [static_cast <R_xlen_t> (where)] += aggr_var [i]; 450 } 451 } 452 453 return aggr_total; 454}