Distances on Directed Graphs in R
at main 402 lines 16 kB view raw
1#include "dodgr-to-sf.h" 2 3//' Make unordered_set of all new edge names 4//' @noRd 5size_t dodgr_sf::make_edge_name_set (std::unordered_set <std::string> &new_edge_name_set, 6 const Rcpp::CharacterVector &new_edges) 7{ 8 new_edge_name_set.clear (); 9 // Rcpp::CharacterVector requires long int index type 10 for (long int i = 0; i < new_edges.size (); i++) 11 { 12 new_edge_name_set.emplace (static_cast <std::string> (new_edges [i])); 13 } 14 return new_edge_name_set.size (); 15} 16 17//' Make vector of all new edge names 18//' @noRd 19void dodgr_sf::make_edge_name_vec (const size_t n, 20 const Rcpp::CharacterVector &new_edges, 21 std::vector <std::string> &new_edge_name_vec) 22{ 23 new_edge_name_vec.clear (); 24 new_edge_name_vec.resize (n); 25 new_edge_name_vec [0] = static_cast <std::string> (new_edges [0]); 26 size_t count = 0; 27 for (long int i = 1; i < new_edges.size (); i++) 28 { 29 std::string new_edge_i = static_cast <std::string> (new_edges [i]); 30 if (new_edge_i != new_edge_name_vec [count]) 31 new_edge_name_vec [++count] = new_edge_i; 32 } 33} 34 35 36//' Get numbers of old edges corresponding to each contracted edge 37//' @noRd 38size_t dodgr_sf::get_edgevec_sizes (const size_t nedges, 39 const Rcpp::CharacterVector &new_edges, 40 std::vector <size_t> &edgevec_sizes) 41{ 42 edgevec_sizes.clear (); 43 edgevec_sizes.resize (nedges); 44 size_t count = 1, edgenum = 0; 45 for (long int i = 1; i < new_edges.size (); i++) 46 { 47 if (new_edges [i] == new_edges [i - 1]) 48 count++; 49 else 50 { 51 edgevec_sizes [edgenum++] = count; 52 count = 1; 53 } 54 } 55 // And last count too: 56 edgevec_sizes [edgenum++] = count; 57 return edgenum; 58} 59 60//' Collect sets of all from and to vertices for each set of edges corresponding 61//' to each contracted edge. These sets aren't in any particular order, but the 62//' two sets may be used to match the from and to vertices. These sets are then 63//' arranged into sequences in the subsequent function, 64//' \code{order_vert_sequences}. 65//' @noRd 66void dodgr_sf::get_edge_to_vert_maps (const std::vector <size_t> &edgevec_sizes, 67 const Rcpp::DataFrame &graph_full, 68 const Rcpp::CharacterVector &old_edges, 69 const Rcpp::CharacterVector &new_edges, 70 const std::vector <std::string> &new_edge_names, 71 std::unordered_map <std::string, 72 std::vector <std::string> > &full_from_edge_map, 73 std::unordered_map <std::string, 74 std::vector <std::string> > &full_to_edge_map) 75{ 76 Rcpp::CharacterVector idf_r = graph_full ["from_id"], 77 idt_r = graph_full ["to_id"]; 78 79 size_t count = 1, edgenum = 0; 80 std::vector <std::string> from_node, to_node; 81 from_node.resize (edgevec_sizes [edgenum]); 82 to_node.resize (edgevec_sizes [edgenum]); 83 // old_edges is 1-indexed! 84 long int the_edge = static_cast <long int> (atoi (old_edges [0])) - 1; 85 from_node [0] = static_cast <std::string> (idf_r [the_edge]); 86 to_node [0] = static_cast <std::string> (idt_r [the_edge]); 87 for (long int i = 1; i < new_edges.size (); i++) 88 { 89 if (new_edges [i] != new_edges [i - 1]) 90 { 91 full_from_edge_map.emplace (new_edge_names [edgenum], from_node); 92 full_to_edge_map.emplace (new_edge_names [edgenum], to_node); 93 from_node.clear (); 94 to_node.clear (); 95 edgenum++; 96 from_node.resize (edgevec_sizes [edgenum]); 97 to_node.resize (edgevec_sizes [edgenum]); 98 count = 0; 99 } 100 the_edge = atoi (old_edges [i]) - 1; // it's 1-indexed! 101 from_node [count] = static_cast <std::string> (idf_r [the_edge]); 102 to_node [count++] = static_cast <std::string> (idt_r [the_edge]); 103 } 104 full_from_edge_map.emplace (new_edge_names [edgenum], from_node); 105 full_to_edge_map.emplace (new_edge_names [edgenum], to_node); 106 107 from_node.clear (); 108 to_node.clear (); 109} 110 111void dodgr_sf::order_vert_sequences (Rcpp::List &edge_sequences, 112 std::vector <std::string> &new_edge_names, 113 std::unordered_map <std::string, 114 std::vector <std::string> > &full_from_edge_map, 115 std::unordered_map <std::string, 116 std::vector <std::string> > &full_to_edge_map) 117{ 118 const size_t nedges = static_cast <size_t> (edge_sequences.size ()); 119 for (size_t i = 0; i < nedges; i++) 120 { 121 Rcpp::checkUserInterrupt (); 122 std::map <std::string, std::string> idmap, idmap_rev; 123 std::string this_edge = new_edge_names [i]; 124 std::vector <std::string> from_edges = full_from_edge_map [this_edge], 125 to_edges = full_to_edge_map [this_edge]; 126 for (size_t j = 0; j < from_edges.size (); j++) 127 { 128 idmap.emplace (from_edges [j], to_edges [j]); 129 idmap_rev.emplace (to_edges [j], from_edges [j]); 130 } 131 size_t nnodes = idmap.size (); 132 133 // Find the front of the sequence of which the first idmap pair is 134 // part by stepping backwards through idmap_rev 135 std::string front_node = idmap.begin ()->first; 136 std::string back_node = idmap.begin ()->second; 137 while (idmap_rev.find (front_node) != idmap_rev.end ()) 138 front_node = idmap_rev.find (front_node)->second; 139 back_node = idmap.find (front_node)->second; 140 141 std::vector <std::string> id (nnodes + 1); 142 id [0] = front_node; 143 id [1] = back_node; 144 size_t count = 2; 145 idmap.erase (front_node); 146 147 while (idmap.size () > 0) 148 { 149 std::map <std::string, std::string>::iterator it = 150 idmap.find (back_node); 151 back_node = it->second; 152 id [count++] = back_node; 153 idmap.erase (it); 154 } 155 idmap_rev.clear (); 156 157 edge_sequences [static_cast <long int> (i)] = id; 158 } 159} 160 161// Count number of non-contracted edges in the contracted graph (non-contracted 162// because they can not be simplified). 163size_t dodgr_sf::count_non_contracted_edges (const Rcpp::CharacterVector &contr_edges, 164 std::unordered_set <std::string> &new_edge_name_set) 165{ 166 size_t edge_count = 0; 167 for (long int i = 0; i < contr_edges.size (); i++) 168 { 169 if (new_edge_name_set.find (static_cast <std::string> 170 (contr_edges [i])) == new_edge_name_set.end ()) 171 { 172 edge_count++; 173 } 174 } 175 return edge_count; 176} 177 178// Append non-contracted edges to contracted edges in all edge-to-vertex maps 179void dodgr_sf::append_nc_edges (const size_t nc_edge_count, 180 const Rcpp::DataFrame &graph_contr, 181 std::unordered_set <std::string> &new_edge_name_set, 182 std::vector <std::string> &new_edge_name_vec, 183 const Rcpp::List &edge_sequences_contr, 184 std::vector <std::string> &all_edge_names, 185 Rcpp::List &edge_sequences_all) 186{ 187 Rcpp::List edge_sequences_new (nc_edge_count); 188 std::vector <std::string> old_edge_names (nc_edge_count); 189 size_t count = 0; 190 Rcpp::CharacterVector idf_r_c = graph_contr ["from_id"], 191 idt_r_c = graph_contr ["to_id"], 192 contr_edges = graph_contr ["edge_id"]; 193 for (long int i = 0; i < graph_contr.nrow (); i++) 194 { 195 if (new_edge_name_set.find (static_cast <std::string> 196 (contr_edges [i])) == new_edge_name_set.end ()) 197 { 198 old_edge_names [count] = contr_edges [i]; 199 Rcpp::CharacterVector idvec (2); 200 idvec [0] = idf_r_c [i]; 201 idvec [1] = idt_r_c [i]; 202 edge_sequences_new [static_cast <long int> (count++)] = idvec; 203 } 204 } 205 // Then just join the two edge_sequence Lists together, along with vectors 206 // of edge names 207 const size_t total_edges = static_cast <size_t> (edge_sequences_contr.size ()) + 208 nc_edge_count; 209 all_edge_names.resize (total_edges); 210 // These two have different indexing types: 211 for (size_t i = 0; i < static_cast <size_t> (edge_sequences_contr.size ()); i++) 212 all_edge_names [i] = new_edge_name_vec [i]; 213 for (long int i = 0; i < edge_sequences_contr.size (); i++) 214 edge_sequences_all [i] = edge_sequences_contr [i]; 215 for (size_t i = 0; i < static_cast <size_t> (edge_sequences_new.size ()); i++) 216 all_edge_names [static_cast <size_t> (edge_sequences_contr.size ()) + i] = 217 old_edge_names [i]; 218 for (long int i = 0; i < edge_sequences_new.size (); i++) 219 edge_sequences_all [edge_sequences_contr.size () + i] = edge_sequences_new [i]; 220} 221 222// from osmdata/src/get-bbox.cpp 223Rcpp::NumericVector rcpp_get_bbox_sf (double xmin, double xmax, double ymin, double ymax) 224{ 225 std::vector <std::string> names; 226 names.push_back ("xmin"); 227 names.push_back ("ymin"); 228 names.push_back ("xmax"); 229 names.push_back ("ymax"); 230 231 Rcpp::NumericVector bbox (4, NA_REAL); 232 bbox (0) = xmin; 233 bbox (1) = xmax; 234 bbox (2) = ymin; 235 bbox (3) = ymax; 236 237 bbox.attr ("names") = names; 238 bbox.attr ("class") = "bbox"; 239 240 return bbox; 241} 242 243// Convert the Rcpp::List of vertex names to corresponding coordinates, 244// construct matrices of sequential coordinates for each contracted edge, and 245// convert these to sf geometry objects 246void dodgr_sf::xy_to_sf (const Rcpp::DataFrame &graph_full, 247 const Rcpp::List &edge_sequences, 248 const std::vector <std::string> &all_edge_names, 249 Rcpp::List &res) 250{ 251 const size_t total_edges = static_cast <size_t> (res.size ()); 252 253 Rcpp::CharacterVector idf_r = graph_full ["from_id"], 254 idt_r = graph_full ["to_id"]; 255 256 Rcpp::NumericVector xf = graph_full ["from_lon"], 257 yf = graph_full ["from_lat"], 258 xt = graph_full ["to_lon"], 259 yt = graph_full ["to_lat"]; 260 261 std::unordered_map <std::string, size_t> edge_num_map; 262 for (long int i = 0; i < idf_r.size (); i++) 263 { 264 std::string idft = static_cast <std::string> (idf_r [i]) + "-" + 265 static_cast <std::string> (idt_r [i]); 266 edge_num_map.emplace (idft, i); 267 } 268 269 double xmin = INFINITE_DOUBLE, xmax = -INFINITE_DOUBLE, 270 ymin = INFINITE_DOUBLE, ymax = -INFINITE_DOUBLE; 271 for (size_t i = 0; i < total_edges; i++) 272 { 273 Rcpp::CharacterVector idvec = edge_sequences [static_cast <long int> (i)]; 274 Rcpp::NumericVector x (idvec.size ()), y (idvec.size ()); 275 // Fill first edge 276 std::string id0 = static_cast <std::string> (idvec [0]), 277 id1 = static_cast <std::string> (idvec [1]); 278 std::string id01 = id0 + "-" + id1; 279 long int j = static_cast <long int> (edge_num_map.find (id01)->second); 280 x [0] = xf [j]; 281 y [0] = yf [j]; 282 x [1] = xt [j]; 283 y [1] = yt [j]; 284 // Then just remaining "to" values 285 idvec.erase (0); 286 idvec.erase (0); 287 long int count = 2; 288 while (idvec.size () > 0) 289 { 290 id0 = id1; 291 id1 = static_cast <std::string> (idvec [0]); 292 id01 = id0 + "-" + id1; 293 j = static_cast <long int> (edge_num_map.find (id01)->second); 294 x [count] = xt [j]; 295 y [count] = yt [j]; 296 count++; 297 idvec.erase (0); 298 } 299 Rcpp::NumericMatrix mat (static_cast <const int> (x.size ()), 2); 300 mat (Rcpp::_, 0) = x; 301 mat (Rcpp::_, 1) = y; 302 xmin = std::min (xmin, static_cast <double> (Rcpp::min (x))); 303 xmax = std::max (xmax, static_cast <double> (Rcpp::max (x))); 304 ymin = std::min (ymin, static_cast <double> (Rcpp::min (y))); 305 ymax = std::max (ymax, static_cast <double> (Rcpp::max (y))); 306 307 // Then some sf necessities: 308 mat.attr ("class") = 309 Rcpp::CharacterVector::create ("XY", "LINESTRING", "sfg"); 310 res (i) = mat; 311 } 312 313 res.attr ("names") = all_edge_names; 314 res.attr ("n_empty") = 0; 315 res.attr ("class") = Rcpp::CharacterVector::create ("sfc_LINESTRING", "sfc"); 316 res.attr ("precision") = 0.0; 317 res.attr ("bbox") = rcpp_get_bbox_sf (xmin, ymin, xmax, ymax); 318 Rcpp::List crs = Rcpp::List::create (static_cast <int> (4326), osm_p4s); 319 crs.attr ("names") = Rcpp::CharacterVector::create ("epsg", "proj4string"); 320 crs.attr ("class") = "crs"; 321 res.attr ("crs") = crs; 322} 323 324//' rcpp_aggregate_to_sf 325//' 326//' Aggregate a dodgr network data.frame to an sf LINESTRING data.frame 327//' 328//' @param graph_full Rcpp::DataFrame containing the **full** graph 329//' @param graph_contr Rcpp::DataFrame containing the **contracted** graph 330//' @param edge_map Rcpp::DataFrame containing the edge map returned from 331//' \code{dodgr_contract_graph} 332//' 333//' @return Rcpp::List object of `sf::LINESTRING` geoms 334//' 335//' @noRd 336// [[Rcpp::export]] 337Rcpp::List rcpp_aggregate_to_sf (const Rcpp::DataFrame &graph_full, 338 const Rcpp::DataFrame &graph_contr, const Rcpp::DataFrame &edge_map) 339{ 340 Rcpp::CharacterVector old_edges, new_edges, 341 contr_edges = graph_contr ["edge_id"]; 342 343 if (edge_map.nrow () > 0) { 344 old_edges = edge_map ["edge_old"]; 345 new_edges = edge_map ["edge_new"]; 346 } else { 347 // If a previously contracted graph is submitted, the edge_map will be 348 // empty and graph_full == graph_contr, so just use the latter only: 349 old_edges = contr_edges; 350 new_edges = contr_edges; 351 } 352 353 // store vector of names used to name the resultant sf-objects. A 354 // corresponding set is also used below to insert non-contracted edges in 355 // final result 356 std::vector <std::string> new_edge_names; 357 std::unordered_set <std::string> new_edge_set; 358 const size_t nedges = dodgr_sf::make_edge_name_set (new_edge_set, new_edges); 359 dodgr_sf::make_edge_name_vec (nedges, new_edges, new_edge_names); 360 361 // Then get numbers of original edges for each contracted edge 362 std::vector <size_t> edgevec_sizes; 363 size_t check = dodgr_sf::get_edgevec_sizes (nedges, new_edges, edgevec_sizes); 364 if (check != nedges) 365 Rcpp::stop ("number of new edges in contracted graph not the right size"); // # nocov 366 367 /* Map contracted edge ids onto corresponding pairs of from and to vertices. 368 * The first vertex of a sequence won't be necessarily at the start of a 369 * sequence, so two maps are made, one from each node to the next, and the 370 * other holding the same values in reverse. The latter enables sequences to 371 * be traced in reverse by matching end points. 372 */ 373 std::unordered_map <std::string, 374 std::vector <std::string> > full_from_edge_map, full_to_edge_map; 375 dodgr_sf::get_edge_to_vert_maps (edgevec_sizes, graph_full, old_edges, new_edges, 376 new_edge_names, full_from_edge_map, full_to_edge_map); 377 edgevec_sizes.clear (); 378 379 Rcpp::List edge_sequences (nedges); // holds final sequences 380 // The main work done in this whole file: 381 dodgr_sf::order_vert_sequences (edge_sequences, new_edge_names, full_from_edge_map, 382 full_to_edge_map); 383 full_from_edge_map.clear (); 384 full_to_edge_map.clear (); 385 386 // Then append the non-contracted edges that are in the contracted graph 387 size_t nc_edge_count = dodgr_sf::count_non_contracted_edges (contr_edges, 388 new_edge_set); 389 std::vector <std::string> all_edge_names; 390 const size_t total_edges = nedges + nc_edge_count; 391 Rcpp::List edge_sequences_all (total_edges); 392 dodgr_sf::append_nc_edges (nc_edge_count, graph_contr, 393 new_edge_set, new_edge_names, edge_sequences, 394 all_edge_names, edge_sequences_all); 395 new_edge_names.clear (); 396 new_edge_set.clear (); 397 398 Rcpp::List res (total_edges); 399 dodgr_sf::xy_to_sf (graph_full, edge_sequences_all, all_edge_names, res); 400 401 return res; 402}