R package for downloading OpenStreetMap data
at main 369 lines 13 kB view raw
1/*************************************************************************** 2 * Project: osmdata 3 * File: osmdata-data_frame.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 * osmdata. If not, see <https://www.gnu.org/licenses/>. 18 * 19 * Author: Mark Padgham 20 * E-Mail: mark.padgham@email.com 21 * 22 * Description: Modified version of 'osmdata-sf' to extract OSM data from an 23 * object of class XmlData and return it in Rcpp::List format, 24 * but in this case ignoring the actual geometric data. The 25 * returned object contains key-value data only, for return 26 * from the R function 'osmdata_data_frame'. 27 * 28 * Limitations: 29 * 30 * Dependencies: none (rapidXML header included in osmdata) 31 * 32 * Compiler Options: -std=c++11 33 ***************************************************************************/ 34 35#include "osmdata.h" 36 37#include <Rcpp.h> 38#include <list> 39#include <string> 40 41// Note: This code uses explicit index counters within most loops which use Rcpp 42// objects, because these otherwise require a 43// static_cast <size_t> (std::distance (...)). This operation copies each 44// instance and can slow the loops down by several orders of magnitude! 45 46/************************************************************************ 47 ************************************************************************ 48 ** ** 49 ** 1. PRIMARY FUNCTIONS TO TRACE WAYS AND RELATIONS ** 50 ** ** 51 ************************************************************************ 52 ************************************************************************/ 53 54 55//' get_osm_relations 56//' 57//' Return a dual Rcpp::DataFrame containing all OSM relations. 58//' 59//' @param rels Pointer to the vector of Relation objects 60//' @param unique_vals Pointer to a UniqueVals object containing std::sets of 61//' all unique IDs and keys for each kind of OSM object (nodes, ways, rels). 62//' 63//' @return A list with three Rcpp::DataFrame with the tags, centers and 64//' metadata of the relations. 65//' 66//' @noRd 67Rcpp::List osm_df::get_osm_relations (const Relations &rels, 68 const UniqueVals &unique_vals) 69{ 70 71 const unsigned int nmp = static_cast <unsigned int> (rels.size ()); 72 Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue); 73 if (nmp == 0L) { 74 return res; 75 } 76 77 size_t ncol = unique_vals.k_rel.size (); 78 std::vector <std::string> rel_ids; 79 rel_ids.reserve (nmp); 80 81 Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nmp, ncol)); 82 std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING); 83 Rcpp::CharacterMatrix meta (Rcpp::Dimension (nmp, 5L)); 84 std::fill (meta.begin (), meta.end (), NA_STRING); 85 Rcpp::NumericMatrix center (Rcpp::Dimension (nmp, 2L)); 86 std::fill (center.begin (), center.end (), NA_REAL); 87 88 unsigned int count = 0; 89 90 for (auto itr = rels.begin (); itr != rels.end (); ++itr) 91 { 92 if (count % 1000 == 0) 93 Rcpp::checkUserInterrupt (); 94 95 rel_ids.push_back (std::to_string (itr->id)); 96 97 meta (count, 0L) = itr->_version; 98 meta (count, 1L) = itr->_timestamp; 99 meta (count, 2L) = itr->_changeset; 100 meta (count, 3L) = itr->_uid; 101 meta (count, 4L) = itr->_user; 102 103 center (count, 0L) = itr->_lat; 104 center (count, 1L) = itr->_lon; 105 106 osm_convert::get_value_mat_rel (itr, unique_vals, kv_mat, count++); 107 } 108 109 Rcpp::DataFrame kv_df; 110 111 kv_mat.attr ("dimnames") = Rcpp::List::create (rel_ids, unique_vals.k_rel); 112 kv_df = osm_convert::restructure_kv_mat (kv_mat, false); 113 114 meta.attr ("dimnames") = Rcpp::List::create (rel_ids, metanames); 115 center.attr ("dimnames") = Rcpp::List::create (rel_ids, centernames); 116 117 res (0) = kv_df; 118 res (1) = meta; 119 res (2) = center; 120 121 rel_ids.clear (); 122 123 return res; 124} 125 126//' get_osm_ways 127//' 128//' Store key-val pairs for OSM ways as a list/data.frame 129//' 130//' @param kv_df Pointer to Rcpp::DataFrame to hold key-value pairs. 131//' @param way_ids Vector of <osmid_t> IDs of ways to trace. 132//' @param ways Pointer to all ways in data set. 133//' @param unique_vals pointer to all unique values (OSM IDs and keys) in data 134//' set. 135//' 136//' @noRd 137Rcpp::List osm_df::get_osm_ways ( 138 const std::set <osmid_t> &way_ids, const Ways &ways, 139 const UniqueVals &unique_vals) 140{ 141 142 size_t nrow = way_ids.size (), ncol = unique_vals.k_way.size (); 143 Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue); 144 if (nrow == 0L) 145 { 146 return res; 147 } 148 149 std::vector <std::string> waynames; 150 waynames.reserve (nrow); 151 152 Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nrow, ncol)); 153 std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING); 154 Rcpp::CharacterMatrix meta (Rcpp::Dimension (nrow, 5L)); 155 std::fill (meta.begin (), meta.end (), NA_STRING); 156 Rcpp::NumericMatrix center (Rcpp::Dimension (nrow, 2L)); 157 std::fill (center.begin (), center.end (), NA_REAL); 158 159 unsigned int count = 0; 160 for (auto wi = way_ids.begin (); wi != way_ids.end (); ++wi) 161 { 162 if (count % 1000 == 0) 163 Rcpp::checkUserInterrupt (); 164 165 waynames.push_back (std::to_string (*wi)); 166 167 auto wj = ways.find (*wi); 168 169 meta (count, 0L) = wj->second._version; 170 meta (count, 1L) = wj->second._timestamp; 171 meta (count, 2L) = wj->second._changeset; 172 meta (count, 3L) = wj->second._uid; 173 meta (count, 4L) = wj->second._user; 174 175 center (count, 0L) = wj->second._lat; 176 center (count, 1L) = wj->second._lon; 177 178 osm_convert::get_value_mat_way (wj, unique_vals, kv_mat, count); 179 count++; 180 } 181 182 Rcpp::DataFrame kv_df = R_NilValue; 183 184 kv_mat.attr ("dimnames") = Rcpp::List::create (waynames, unique_vals.k_way); 185 if (kv_mat.nrow () > 0 && kv_mat.ncol () > 0) 186 kv_df = osm_convert::restructure_kv_mat (kv_mat, false); 187 188 meta.attr ("dimnames") = Rcpp::List::create (waynames, metanames); 189 center.attr ("dimnames") = Rcpp::List::create (waynames, centernames); 190 191 res (0) = kv_df; 192 res (1) = meta; 193 res (2) = center; 194 195 waynames.clear (); 196 197 return res; 198} 199 200//' get_osm_nodes 201//' 202//' Store OSM nodes as `sf::POINT` objects 203//' 204//' @param kv_df Pointer to Rcpp::DataFrame to hold key-value pairs. 205//' @param nodes Pointer to all nodes in data set. 206//' @param unique_vals pointer to all unique values (OSM IDs and keys) in data 207//' set. 208//' 209//' @noRd 210Rcpp::List osm_df::get_osm_nodes (const Nodes &nodes, 211 const UniqueVals &unique_vals) 212{ 213 size_t nrow = nodes.size (), ncol = unique_vals.k_point.size (); 214 215 Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nrow, ncol)); 216 std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING); 217 Rcpp::CharacterMatrix meta (Rcpp::Dimension (nrow, 5L)); 218 std::fill (meta.begin (), meta.end (), NA_STRING); 219 Rcpp::NumericMatrix center (Rcpp::Dimension (nrow, 2L)); 220 std::fill (center.begin (), center.end (), NA_REAL); 221 222 const size_t n = nodes.size (); 223 std::vector <std::string> ptnames; 224 ptnames.reserve (n); 225 226 unsigned int count = 0; 227 for (auto ni = nodes.begin (); ni != nodes.end (); ++ni) 228 { 229 if (count % 1000 == 0) 230 Rcpp::checkUserInterrupt (); 231 232 ptnames.push_back (std::to_string (ni->first)); 233 234 meta (count, 0L) = ni->second._version; 235 meta (count, 1L) = ni->second._timestamp; 236 meta (count, 2L) = ni->second._changeset; 237 meta (count, 3L) = ni->second._uid; 238 meta (count, 4L) = ni->second._user; 239 240 center(count, 0L) = ni->second.lat; 241 center(count, 1L) = ni->second.lon; 242 243 for (auto kv_iter = ni->second.key_val.begin (); 244 kv_iter != ni->second.key_val.end (); ++kv_iter) 245 { 246 const std::string &key = kv_iter->first; 247 unsigned int ndi = unique_vals.k_point_index.at (key); 248 kv_mat (count, ndi) = kv_iter->second; 249 } 250 count++; 251 } 252 253 Rcpp::DataFrame kv_df = R_NilValue; 254 Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue); 255 256 if (unique_vals.k_point.size () > 0) 257 { 258 kv_mat.attr ("dimnames") = Rcpp::List::create (ptnames, unique_vals.k_point); 259 kv_df = osm_convert::restructure_kv_mat (kv_mat, false); 260 261 meta.attr ("dimnames") = Rcpp::List::create (ptnames, metanames); 262 center.attr ("dimnames") = Rcpp::List::create (ptnames, centernames); 263 264 res (0) = kv_df; 265 res (1) = meta; 266 res (2) = center; 267 } 268 269 ptnames.clear (); 270 271 return res; 272} 273 274 275/************************************************************************ 276 ************************************************************************ 277 ** ** 278 ** THE FINAL RCPP FUNCTION CALLED BY osmdata_df ** 279 ** ** 280 ************************************************************************ 281 ************************************************************************/ 282 283//' rcpp_osmdata_df 284//' 285//' Return OSM data key-value pairs in series of data.frame objects, without any 286//' spatial/geometrtic information. 287//' 288//' @param st Text contents of an overpass API query 289//' @return Rcpp::List objects of OSM data 290//' 291//' @noRd 292// [[Rcpp::export]] 293Rcpp::List rcpp_osmdata_df (const std::string& st) 294{ 295 XmlData xml (st); 296 297 const std::map <osmid_t, Node>& nodes = xml.nodes (); 298 const std::map <osmid_t, OneWay>& ways = xml.ways (); 299 const std::vector <Relation>& rels = xml.relations (); 300 const UniqueVals& unique_vals = xml.unique_vals (); 301 302 Rcpp::DataFrame kv_rels, kv_df_ways, kv_df_points; 303 Rcpp::CharacterMatrix meta_rels, meta_ways, meta_nodes; 304 Rcpp::NumericMatrix center_rels, center_ways, center_nodes; 305 306 /* -------------------------------------------------------------- 307 * 1. Extract OSM Relations 308 * --------------------------------------------------------------*/ 309 310 Rcpp::List data_rels = osm_df::get_osm_relations (rels, unique_vals); 311 if (data_rels (0) != R_NilValue) 312 { 313 kv_rels = Rcpp::as <Rcpp::DataFrame> (data_rels (0)); 314 meta_rels = Rcpp::as <Rcpp::CharacterMatrix> (data_rels (1)); 315 center_rels = Rcpp::as <Rcpp::NumericMatrix> (data_rels (2)); 316 } 317 318 /* -------------------------------------------------------------- 319 * 2. Extract OSM ways 320 * --------------------------------------------------------------*/ 321 322 std::set <osmid_t> way_ids; 323 for (auto itw = ways.begin (); itw != ways.end (); ++itw) 324 { 325 way_ids.insert ((*itw).first); 326 } 327 328 Rcpp::List data_ways = osm_df::get_osm_ways (way_ids, ways, unique_vals); 329 if (data_ways (0) != R_NilValue) 330 { 331 kv_df_ways = Rcpp::as <Rcpp::DataFrame> (data_ways (0)); 332 meta_ways = Rcpp::as <Rcpp::CharacterMatrix> (data_ways (1)); 333 center_ways = Rcpp::as <Rcpp::NumericMatrix> (data_ways (2)); 334 } 335 336 /* -------------------------------------------------------------- 337 * 3. Extract OSM nodes 338 * --------------------------------------------------------------*/ 339 340 Rcpp::List data_nodes = osm_df::get_osm_nodes (nodes, unique_vals); 341 if (data_nodes (0) != R_NilValue) 342 { 343 kv_df_points = Rcpp::as <Rcpp::DataFrame> (data_nodes (0)); 344 meta_nodes = Rcpp::as <Rcpp::CharacterMatrix> (data_nodes (1)); 345 center_nodes = Rcpp::as <Rcpp::NumericMatrix> (data_nodes (2)); 346 } 347 348 /* -------------------------------------------------------------- 349 * 4. Collate all data 350 * --------------------------------------------------------------*/ 351 352 Rcpp::List ret (9); 353 ret [0] = kv_df_points; 354 ret [1] = kv_df_ways; 355 ret [2] = kv_rels; 356 ret [3] = meta_nodes; 357 ret [4] = meta_ways; 358 ret [5] = meta_rels; 359 ret [6] = center_nodes; 360 ret [7] = center_ways; 361 ret [8] = center_rels; 362 363 std::vector <std::string> retnames {"points_kv", "ways_kv", "rels_kv", 364 "points_meta", "ways_meta", "rels_meta", 365 "points_center", "ways_center", "rels_center"}; 366 ret.attr ("names") = retnames; 367 368 return ret; 369}