R package for downloading OpenStreetMap data
at main 559 lines 23 kB view raw
1/*************************************************************************** 2 * Project: osmdata 3 * File: convert_osm_rcpp.cpp 4 * Language: C++ 5 * 6 * osmdata is free software: you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License as published by the Free 8 * Software Foundation, either version 3 of the License, or (at your option) 9 * any later version. 10 * 11 * osmdata is distributed in the hope that it will be useful, but WITHOUT ANY 12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 * details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * osm-router. If not, see <http://www.gnu.org/licenses/>. 18 * 19 * Author: Mark Padgham 20 * E-Mail: mark.padgham@email.com 21 * 22 * Description: Functions to convert OSM data stored in C++ arrays to Rcpp 23 * structures. 24 * 25 * Limitations: 26 * 27 * Dependencies: none (rapidXML header included in osmdatar) 28 * 29 * Compiler Options: -std=c++11 30 ***************************************************************************/ 31 32#include "convert-osm-rcpp.h" 33 34 35/************************************************************************ 36 ************************************************************************ 37 ** ** 38 ** FUNCTIONS TO CONVERT C++ OBJECTS TO Rcpp::List OBJECTS ** 39 ** ** 40 ************************************************************************ 41 ************************************************************************/ 42 43/* Traces a single way and adds (lon,lat,rownames) to an Rcpp::NumericMatrix 44 * 45 * @param &ways pointer to Ways structure 46 * @param &nodes pointer to Nodes structure 47 * @param first_node Last node of previous way to find in current 48 * @param &wayi_id pointer to ID of current way 49 * @lons pointer to vector of longitudes 50 * @lats pointer to vector of latitutdes 51 * @rownames pointer to vector of rownames for each node. 52 * @nmat Rcpp::NumericMatrix to store lons, lats, and rownames 53 */ 54void osm_convert::trace_way_nmat (const Ways &ways, const Nodes &nodes, 55 const osmid_t &wayi_id, Rcpp::NumericMatrix &nmat) 56{ 57 auto wayi = ways.find (wayi_id); 58 std::vector <std::string> rownames; 59 rownames.clear (); 60 size_t n = wayi->second.nodes.size (); 61 rownames.reserve (n); 62 nmat = Rcpp::NumericMatrix (Rcpp::Dimension (n, 2)); 63 64 size_t tempi = 0; 65 for (auto ni = wayi->second.nodes.begin (); 66 ni != wayi->second.nodes.end (); ++ni) 67 { 68 rownames.push_back (std::to_string (*ni)); 69 //nmat (tempi, 0) = static_cast <double> (nodes.find (*ni)->second.lon); 70 //nmat (tempi++, 1) = static_cast <double> (nodes.find (*ni)->second.lat); 71 nmat (tempi, 0) = nodes.find (*ni)->second.lon; 72 nmat (tempi++, 1) = nodes.find (*ni)->second.lat; 73 } 74 75 std::vector <std::string> colnames = {"lon", "lat"}; 76 Rcpp::List dimnames (0); 77 dimnames.push_back (rownames); 78 dimnames.push_back (colnames); 79 nmat.attr ("dimnames") = dimnames; 80 //dimnames.erase (0, static_cast <int> (dimnames.size ())); 81 dimnames.erase (0, 2); 82} 83 84/* get_value_mat_way 85 * 86 * Extract key-value pairs for a given way and fill Rcpp::CharacterMatrix 87 * 88 * @param wayi Constant iterator to one OSM way 89 * @param Ways Pointer to the std::vector of all ways 90 * @param unique_vals Pointer to the UniqueVals structure 91 * @param value_arr Pointer to the Rcpp::CharacterMatrix of values to be filled 92 * by tracing the key-val pairs of the way 'wayi' 93 * @param rowi Integer value for the key-val pairs for wayi 94 */ 95// TODO: Is it faster to use a std::vector <std::string> instead of 96// Rcpp::CharacterMatrix and then simply 97// Rcpp::CharacterMatrix mat (nrow, ncol, value_vec.begin ()); ? 98void osm_convert::get_value_mat_way (Ways::const_iterator wayi, 99 const UniqueVals &unique_vals, Rcpp::CharacterMatrix &value_arr, 100 unsigned int rowi) 101{ 102 for (auto kv_iter = wayi->second.key_val.begin (); 103 kv_iter != wayi->second.key_val.end (); ++kv_iter) 104 { 105 const std::string &key = kv_iter->first; 106 unsigned int coli = unique_vals.k_way_index.at (key); 107 value_arr (rowi, coli) = kv_iter->second; 108 } 109} 110 111/* get_value_mat_rel 112 * 113 * Extract key-value pairs for a given relation and fill Rcpp::CharacterMatrix 114 * 115 * @param reli Constant iterator to one OSM relation 116 * @param rels Pointer to the std::vector of all relations 117 * @param unique_vals Pointer to the UniqueVals structure 118 * @param value_arr Pointer to the Rcpp::CharacterMatrix of values to be filled 119 * by tracing the key-val pairs of the relation 'reli' 120 * @param rowi Integer value for the key-val pairs for reli 121 */ 122void osm_convert::get_value_mat_rel (Relations::const_iterator &reli, 123 const UniqueVals &unique_vals, Rcpp::CharacterMatrix &value_arr, 124 unsigned int rowi) 125{ 126 for (auto kv_iter = reli->key_val.begin (); kv_iter != reli->key_val.end (); 127 ++kv_iter) 128 { 129 const std::string &key = kv_iter->first; 130 unsigned int coli = unique_vals.k_rel_index.at (key); 131 value_arr (rowi, coli) = kv_iter->second; 132 } 133} 134 135 136/* restructure_kv_mat 137 * 138 * Restructures a key-value matrix to reflect typical GDAL output by 139 * inserting a first column containing "osm_id" and moving the "name" column to 140 * second position. 141 * 142 * @param kv Pointer to the key-value matrix to be restructured 143 * @param ls true only for multilinestrings, which have compound rownames which 144 * include roles 145 */ 146Rcpp::CharacterMatrix osm_convert::restructure_kv_mat (Rcpp::CharacterMatrix &kv, bool ls=false) 147{ 148 // The following has to be done in 2 lines: 149 std::vector <std::vector <std::string> > dims = kv.attr ("dimnames"); 150 std::vector <std::string> ids = dims [0], varnames = dims [1], varnames_new; 151 Rcpp::CharacterMatrix kv_out; 152 153 int ni = static_cast <int> (std::distance (varnames.begin (), 154 std::find (varnames.begin (), varnames.end (), "name"))); 155 unsigned int add_lines = 1; 156 if (ls) 157 add_lines++; 158 159 // This only processes entries that have key = "name". Those that don't do 160 // not get their osm_id values appended. It's then easier to post-append 161 // these, rather than modify this code 162 if (ni < static_cast <int> (varnames.size ())) 163 { 164 Rcpp::CharacterVector name_vals = kv.column (ni); 165 Rcpp::CharacterVector roles; // only for ls, but has to be defined here 166 // convert ids to CharacterVector - direct allocation doesn't work 167 Rcpp::CharacterVector ids_rcpp (ids.size ()); 168 if (!ls) 169 for (unsigned int i=0; i<ids.size (); i++) 170 ids_rcpp (i) = ids [i]; 171 else 172 { // extract way roles for multilinestring kev-val matrices 173 roles = Rcpp::CharacterVector (ids.size ()); 174 for (unsigned int i=0; i<ids.size (); i++) 175 { 176 size_t ipos = ids [i].find ("-", 0); 177 ids_rcpp (i) = ids [i].substr (0, ipos).c_str (); 178 roles (i) = ids [i].substr (ipos + 1, ids[i].length () - ipos); 179 } 180 } 181 182 varnames_new.reserve (static_cast <unsigned int> (kv.ncol ()) + 183 add_lines); 184 varnames_new.push_back ("osm_id"); 185 varnames_new.push_back ("name"); 186 kv_out = Rcpp::CharacterMatrix (Rcpp::Dimension ( 187 static_cast <unsigned int> (kv.nrow ()), 188 static_cast <unsigned int> (kv.ncol ()) + add_lines)); 189 kv_out.column (0) = ids_rcpp; 190 kv_out.column (1) = name_vals; 191 if (ls) 192 { 193 varnames_new.push_back ("role"); 194 kv_out.column (2) = roles; 195 } 196 int count = 1 + static_cast <int> (add_lines); 197 int i_int = 0; 198 for (unsigned int i=0; i<static_cast <unsigned int> (kv.ncol ()); i++) 199 { 200 if (i != static_cast <unsigned int> (ni)) 201 { 202 varnames_new.push_back (varnames [i]); 203 kv_out.column (count++) = kv.column (i_int); 204 } 205 i_int++; 206 } 207 kv_out.attr ("dimnames") = Rcpp::List::create (ids, varnames_new); 208 } else 209 kv_out = kv; 210 211 return kv_out; 212} 213 214/* convert_poly_linestring_to_sf 215 * 216 * Converts the data contained in all the arguments into an Rcpp::List object 217 * to be used as the geometry column of a Simple Features Colletion 218 * 219 * @param lon_arr 3D array of longitudinal coordinates 220 * @param lat_arr 3D array of latgitudinal coordinates 221 * @param rowname_arr 3D array of <osmid_t> IDs for nodes of all (lon,lat) 222 * @param id_vec 2D array of either <std::string> or <osmid_t> IDs for all ways 223 * used in the geometry. 224 * @param rel_id Vector of <osmid_t> IDs for each relation. 225 * 226 * @return An Rcpp::List object of [relation][way][node/geom] data. 227 */ 228// TODO: Replace return value with pointer to List as argument? 229template <typename T> Rcpp::List osm_convert::convert_poly_linestring_to_sf ( 230 const double_arr3 &lon_arr, const double_arr3 &lat_arr, 231 const string_arr3 &rowname_arr, 232 const std::vector <std::vector <T> > &id_vec, 233 const std::vector <std::string> &rel_id, const std::string type) 234{ 235 if (!(type == "MULTILINESTRING" || type == "MULTIPOLYGON")) 236 throw std::runtime_error ("type must be multilinestring/polygon"); // # nocov 237 Rcpp::List outList (lon_arr.size ()); 238 Rcpp::NumericMatrix nmat (Rcpp::Dimension (0, 0)); 239 Rcpp::List dimnames (0); 240 std::vector <std::string> colnames = {"lat", "lon"}; 241 for (unsigned int i=0; i<lon_arr.size (); i++) // over all relations 242 { 243 Rcpp::List outList_i (lon_arr [i].size ()); 244 for (unsigned int j=0; j<lon_arr [i].size (); j++) // over all ways 245 { 246 size_t n = lon_arr [i][j].size (); 247 nmat = Rcpp::NumericMatrix (Rcpp::Dimension (n, 2)); 248 std::copy (lon_arr [i][j].begin (), lon_arr [i][j].end (), 249 nmat.begin ()); 250 std::copy (lat_arr [i][j].begin (), lat_arr [i][j].end (), 251 nmat.begin () + n); 252 dimnames.push_back (rowname_arr [i][j]); 253 dimnames.push_back (colnames); 254 nmat.attr ("dimnames") = dimnames; 255 //dimnames.erase (0, static_cast <int> (dimnames.size ())); 256 dimnames.erase (0, 2); 257 outList_i [j] = nmat; 258 } 259 outList_i.attr ("names") = id_vec [i]; 260 if (type == "MULTIPOLYGON") 261 { 262 Rcpp::List tempList (1); 263 tempList (0) = outList_i; 264 tempList.attr ("class") = Rcpp::CharacterVector::create ("XY", type, "sfg"); 265 outList [i] = tempList; 266 } else 267 { 268 outList_i.attr ("class") = Rcpp::CharacterVector::create ("XY", type, "sfg"); 269 outList [i] = outList_i; 270 } 271 } 272 outList.attr ("names") = rel_id; 273 274 return outList; 275} 276template Rcpp::List osm_convert::convert_poly_linestring_to_sf <osmid_t> ( 277 const double_arr3 &lon_arr, const double_arr3 &lat_arr, 278 const string_arr3 &rowname_arr, 279 const std::vector <std::vector <osmid_t> > &id_vec, 280 const std::vector <std::string> &rel_id, const std::string type); 281template Rcpp::List osm_convert::convert_poly_linestring_to_sf <std::string> ( 282 const double_arr3 &lon_arr, const double_arr3 &lat_arr, 283 const string_arr3 &rowname_arr, 284 const std::vector <std::vector <std::string> > &id_vec, 285 const std::vector <std::string> &rel_id, const std::string type); 286 287/* convert_multipoly_to_sp 288 * 289 * Converts the data contained in all the arguments into a 290 * SpatialPolygonsDataFrame 291 * 292 * @param lon_arr 3D array of longitudinal coordinates 293 * @param lat_arr 3D array of latgitudinal coordinates 294 * @param rowname_arr 3D array of <osmid_t> IDs for nodes of all (lon,lat) 295 * @param id_vec 2D array of either <std::string> or <osmid_t> IDs for all ways 296 * used in the geometry. 297 * @param rel_id Vector of <osmid_t> IDs for each relation. 298 * 299 * @return Object pointed to by 'multipolygons' is constructed. 300 */ 301void osm_convert::convert_multipoly_to_sp (Rcpp::S4 &multipolygons, const Relations &rels, 302 const double_arr3 &lon_arr, const double_arr3 &lat_arr, 303 const string_arr3 &rowname_arr, const string_arr2 &id_vec, 304 const UniqueVals &unique_vals) 305{ 306 Rcpp::Environment sp_env = Rcpp::Environment::namespace_env ("sp"); 307 Rcpp::Function Polygon = sp_env ["Polygon"]; 308 Rcpp::Language polygons_call ("new", "Polygons"); 309 310 size_t nrow = lon_arr.size (), ncol = unique_vals.k_rel.size (); 311 Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nrow, ncol)); 312 std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING); 313 314 Rcpp::List outList (lon_arr.size ()); 315 Rcpp::NumericMatrix nmat (Rcpp::Dimension (0, 0)); 316 Rcpp::List dimnames (0); 317 std::vector <std::string> colnames = {"lat", "lon"}, rel_id; 318 319 unsigned int npolys = 0; 320 for (auto itr = rels.begin (); itr != rels.end (); itr++) 321 if (itr->ispoly) 322 npolys++; 323 if (npolys != lon_arr.size ()) 324 throw std::runtime_error ("polygons must be same size as geometries"); // # nocov 325 rel_id.reserve (npolys); 326 327 unsigned int i = 0; 328 for (auto itr = rels.begin (); itr != rels.end (); itr++) 329 if (itr->ispoly) 330 { 331 Rcpp::List outList_i (lon_arr [i].size ()); 332 // j over all ways, with outer always first followed by inner 333 bool outer = true; 334 //std::vector <int> plotorder (lon_arr [i].size ()); 335 Rcpp::IntegerVector plotorder (lon_arr [i].size ()); 336 for (unsigned int j=0; j<lon_arr [i].size (); j++) 337 { 338 size_t n = lon_arr [i][j].size (); 339 nmat = Rcpp::NumericMatrix (Rcpp::Dimension (n, 2)); 340 std::copy (lon_arr [i][j].begin (), lon_arr [i][j].end (), 341 nmat.begin ()); 342 std::copy (lat_arr [i][j].begin (), lat_arr [i][j].end (), 343 nmat.begin () + n); 344 dimnames.push_back (rowname_arr [i][j]); 345 dimnames.push_back (colnames); 346 nmat.attr ("dimnames") = dimnames; 347 //dimnames.erase (0, static_cast <int> (dimnames.size ())); 348 dimnames.erase (0, 2); 349 350 Rcpp::S4 poly = Polygon (nmat); 351 poly.slot ("hole") = !outer; 352 poly.slot ("ringDir") = static_cast <int> (1); 353 if (outer) 354 outer = false; 355 else 356 poly.slot ("ringDir") = static_cast <int> (-1); 357 outList_i [j] = poly; 358 plotorder [j] = static_cast <int> (j) + 1; // 1-based R values 359 } 360 outList_i.attr ("names") = id_vec [i]; 361 362 Rcpp::S4 polygons = polygons_call.eval (); 363 polygons.slot ("Polygons") = outList_i; 364 // Issue #36 caused by data with one item having no actual data for 365 // one item, so id_vec[i].size = lon_vec[i].size = ... = 0 366 if (id_vec [i].size () > 0) 367 { 368 // convert id_vec to single string 369 std::string id_vec_str = id_vec [i] [0]; 370 for (unsigned int j = 1; 371 j < static_cast <unsigned int> (id_vec [i].size ()); j++) 372 id_vec_str += "." + id_vec [i] [j]; 373 polygons.slot ("ID") = id_vec_str; 374 } 375 //polygons.slot ("ID") = id_vec [i]; // sp expects char not vec! 376 polygons.slot ("plotOrder") = plotorder; 377 //polygons.slot ("labpt") = poly.slot ("labpt"); 378 //polygons.slot ("area") = poly.slot ("area"); 379 outList [i] = polygons; 380 rel_id.push_back (std::to_string (itr->id)); 381 382 osm_convert::get_value_mat_rel (itr, unique_vals, kv_mat, i++); 383 } // end if ispoly & for i 384 outList.attr ("names") = rel_id; 385 386 Rcpp::Language sp_polys_call ("new", "SpatialPolygonsDataFrame"); 387 multipolygons = sp_polys_call.eval (); 388 multipolygons.slot ("polygons") = outList; 389 390 // Fill plotOrder slot with int vector - this has to be int, not 391 // unsigned int! 392 std::vector <int> plotord (rels.size ()); 393 for (size_t j = 0; j < rels.size (); j++) 394 plotord [j] = static_cast <int> (j + 1); 395 multipolygons.slot ("plotOrder") = plotord; 396 plotord.clear (); 397 398 Rcpp::DataFrame kv_df = R_NilValue; 399 if (rel_id.size () > 0) 400 { 401 kv_mat.attr ("names") = unique_vals.k_rel; 402 kv_mat.attr ("dimnames") = Rcpp::List::create (rel_id, unique_vals.k_rel); 403 kv_mat.attr ("names") = unique_vals.k_rel; 404 if (kv_mat.nrow () > 0 && kv_mat.ncol () > 0) 405 kv_df = osm_convert::restructure_kv_mat (kv_mat, false); 406 multipolygons.slot ("data") = kv_df; 407 } 408 rel_id.clear (); 409} 410 411 412/* convert_multiline_to_sp 413 * 414 * Converts the data contained in all the arguments into a SpatialLinesDataFrame 415 * 416 * @param lon_arr 3D array of longitudinal coordinates 417 * @param lat_arr 3D array of latgitudinal coordinates 418 * @param rowname_arr 3D array of <osmid_t> IDs for nodes of all (lon,lat) 419 * @param id_vec 2D array of either <std::string> or <osmid_t> IDs for all ways 420 * used in the geometry. 421 * @param rel_id Vector of <osmid_t> IDs for each relation. 422 * 423 * @return Object pointed to by 'multilines' is constructed. 424 */ 425void osm_convert::convert_multiline_to_sp (Rcpp::S4 &multilines, const Relations &rels, 426 const double_arr3 &lon_arr, const double_arr3 &lat_arr, 427 const string_arr3 &rowname_arr, const osmt_arr2 &id_vec, 428 const UniqueVals &unique_vals) 429{ 430 431 Rcpp::Language line_call ("new", "Line"); 432 Rcpp::Language lines_call ("new", "Lines"); 433 434 Rcpp::NumericMatrix nmat (Rcpp::Dimension (0, 0)); 435 Rcpp::List dimnames (0); 436 std::vector <std::string> colnames = {"lat", "lon"}, rel_id; 437 438 unsigned int nlines = 0; 439 for (auto itr = rels.begin (); itr != rels.end (); itr++) 440 if (!itr->ispoly) 441 nlines++; 442 rel_id.reserve (nlines); 443 444 Rcpp::List outList (nlines); 445 size_t ncol = unique_vals.k_rel.size (); 446 Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nlines, ncol)); 447 std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING); 448 449 unsigned int i = 0; 450 for (auto itr = rels.begin (); itr != rels.end (); itr++) 451 if (!itr->ispoly) 452 { 453 Rcpp::List outList_i (lon_arr [i].size ()); 454 // j over all ways 455 for (unsigned int j=0; j<lon_arr [i].size (); j++) 456 { 457 size_t n = lon_arr [i][j].size (); 458 nmat = Rcpp::NumericMatrix (Rcpp::Dimension (n, 2)); 459 std::copy (lon_arr [i][j].begin (), lon_arr [i][j].end (), 460 nmat.begin ()); 461 std::copy (lat_arr [i][j].begin (), lat_arr [i][j].end (), 462 nmat.begin () + n); 463 dimnames.push_back (rowname_arr [i][j]); 464 dimnames.push_back (colnames); 465 nmat.attr ("dimnames") = dimnames; 466 //dimnames.erase (0, static_cast <int> (dimnames.size ())); 467 dimnames.erase (0, 2); 468 469 Rcpp::S4 line = line_call.eval (); 470 line.slot ("coords") = nmat; 471 472 outList_i [j] = line; 473 } 474 outList_i.attr ("names") = id_vec [i]; // implicit type conversion 475 Rcpp::S4 lines = lines_call.eval (); 476 lines.slot ("Lines") = outList_i; 477 lines.slot ("ID") = itr->id; 478 479 outList [i] = lines; 480 rel_id.push_back (std::to_string (itr->id)); 481 482 osm_convert::get_value_mat_rel (itr, unique_vals, kv_mat, i++); 483 } // end if ispoly & for i 484 outList.attr ("names") = rel_id; 485 486 Rcpp::Language sp_lines_call ("new", "SpatialLinesDataFrame"); 487 multilines = sp_lines_call.eval (); 488 multilines.slot ("lines") = outList; 489 490 Rcpp::DataFrame kv_df = R_NilValue; 491 if (rel_id.size () > 0) 492 { 493 kv_mat.attr ("names") = unique_vals.k_rel; 494 kv_mat.attr ("dimnames") = Rcpp::List::create (rel_id, unique_vals.k_rel); 495 kv_mat.attr ("names") = unique_vals.k_rel; 496 if (kv_mat.nrow () > 0 && kv_mat.ncol () > 0) 497 kv_df = osm_convert::restructure_kv_mat (kv_mat, true); 498 multilines.slot ("data") = kv_df; 499 } 500 rel_id.clear (); 501} 502 503/* convert_relation_to_sc 504 * 505 * Converts the data contained in all the arguments into an SC object 506 * 507 * @param id_vec 2D array of either <std::string> or <osmid_t> IDs for all ways 508 * used in the geometry. 509 * 510 * @return Objects pointed to by 'members_out' and 'kv_out' are constructed. 511 */ 512void osm_convert::convert_relation_to_sc (string_arr2 &members_out, 513 string_arr2 &kv_out, const Relations &rels, 514 const UniqueVals &unique_vals) 515{ 516 size_t nmembers = 0; 517 for (auto itr = rels.begin (); itr != rels.end (); itr++) 518 nmembers += itr->relations.size (); 519 520 members_out.resize (nmembers); 521 for (auto m: members_out) 522 m.resize (3); 523 524 size_t ncol = unique_vals.k_rel.size (); 525 kv_out.resize (ncol); 526 for (auto k: kv_out) 527 k.resize (rels.size ()); 528 529 unsigned int rowi = 0; // explicit loop index - see note at top of osmdata-sf 530 for (auto itr = rels.begin (); itr != rels.end (); itr++) 531 { 532 //const unsigned int rowi = static_cast <unsigned int> ( 533 // std::distance (rels.begin (), itr)); 534 535 // Get all members of that relation and their roles: 536 unsigned int rowj = rowi; 537 for (auto ritr = itr->relations.begin (); 538 ritr != itr->relations.end (); ++ritr) 539 { 540 //const unsigned int rowj = rowi + static_cast <unsigned int> ( 541 // std::distance (itr->relations.begin (), ritr)); 542 members_out [rowj] [0] = std::to_string (itr->id); 543 members_out [rowj] [1] = std::to_string (ritr->first); // OSM id 544 members_out [rowj++] [2] = ritr->second; // role 545 } 546 547 // And then key-value pairs 548 for (auto kv_iter = itr->key_val.begin (); 549 kv_iter != itr->key_val.end (); ++kv_iter) 550 { 551 const std::string &key = kv_iter->first; 552 //long int coli = static_cast <long int> ( 553 // unique_vals.k_rel_index.at (key)); 554 unsigned int coli = unique_vals.k_rel_index.at (key); 555 kv_out [coli] [rowi] = kv_iter->second; 556 } 557 rowi++; 558 } // end for itr 559}