R package for downloading OpenStreetMap data
at main 292 lines 11 kB view raw
1/*************************************************************************** 2 * Project: osmdata 3 * File: trace-osm.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 <https://www.gnu.org/licenses/>. 18 * 19 * Author: Mark Padgham 20 * E-Mail: mark.padgham@email.com 21 * 22 * Description: Functions to trace OSM ways and store in C++ dynamic arrays 23 * (no RCpp here). 24 * 25 * Limitations: 26 * 27 * Dependencies: none (rapidXML header included in osmdata) 28 * 29 * Compiler Options: -std=c++11 30 ***************************************************************************/ 31 32#include "trace-osm.h" 33 34/* Traces a single relation of any type (SC only) 35 * 36 * @param itr_rel iterator to XmlData::Relations structure 37 */ 38void trace_relation (Relations::const_iterator &itr_rel, 39 osm_str_vec &relation_ways, 40 std::vector <std::pair <std::string, std::string> > & relation_kv) 41{ 42 relation_ways.reserve (itr_rel->ways.size ()); 43 for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) 44 relation_ways.push_back (std::make_pair (itw->first, itw->second)); 45 46 relation_kv.reserve (itr_rel->key_val.size ()); 47 for (auto itk = itr_rel->key_val.begin (); 48 itk != itr_rel->key_val.end (); ++itk) 49 relation_kv.push_back (std::make_pair (itk->first, itk->second)); 50} 51 52 53/* Traces a single multipolygon relation 54 * 55 * @param itr_rel iterator to XmlData::Relations structure 56 * @param &ways pointer to Ways structure 57 * @param &nodes pointer to Nodes structure 58 * @param &lon_vec pointer to 2D array of longitudes 59 * @param &lat_vec pointer to 2D array of latitudes 60 * @param &rowname_vec pointer to 2D array of rownames for each node. 61 * @param &id_vec pointer to 2D array of OSM IDs for each way in relation 62 */ 63void trace_multipolygon (Relations::const_iterator &itr_rel, const Ways &ways, 64 const Nodes &nodes, double_arr2 &lon_vec, double_arr2 &lat_vec, 65 string_arr2 &rowname_vec, std::vector <std::string> &ids) 66{ 67 bool closed, ptr_check; 68 osmid_t node0, first_node, last_node; 69 std::string this_role; 70 std::stringstream this_way; 71 std::vector <double> lons, lats; 72 std::vector <std::string> rownames, wayname_vec; 73 74 osm_str_vec relation_ways; 75 relation_ways.reserve (itr_rel->ways.size ()); 76 for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) 77 relation_ways.push_back (std::make_pair (itw->first, itw->second)); 78 it_osm_str_vec itr_rw; 79 80 bool way_okay = true; 81 // Then trace through all those relations and store associated data 82 while (relation_ways.size () > 0) 83 { 84 auto rwi = relation_ways.begin (); 85 if (rwi->second != "outer") // "outer" role first 86 { 87 while (std::distance (rwi, relation_ways.end ()) > 1) 88 { 89 std::advance (rwi, 1); 90 if (rwi->second == "outer") 91 break; 92 } 93 if (rwi->second != "outer") // just reset to first 94 rwi = relation_ways.begin (); 95 } 96 this_role = rwi->second; 97 auto wayi = ways.find (rwi->first); 98 if (wayi == ways.end ()) 99 throw std::runtime_error ("way can not be found"); 100 this_way.str (""); 101 this_way << std::to_string (rwi->first); 102 relation_ways.erase (rwi); 103 104 // Get first way of relation, and starting node 105 node0 = wayi->second.nodes.front (); 106 last_node = trace_way (ways, nodes, node0, 107 wayi->first, lons, lats, rownames, false); 108 closed = false; 109 if (last_node == node0) 110 closed = true; 111 while (!closed) 112 { 113 first_node = last_node; 114 ptr_check = false; 115 for (auto itw = relation_ways.begin (); 116 itw != relation_ways.end (); ++itw) 117 { 118 if (itw->second == this_role) 119 { 120 auto wayj = ways.find (itw->first); 121 if (wayj == ways.end ()) 122 throw std::runtime_error ("way can not be found"); 123 last_node = trace_way (ways, nodes, first_node, 124 wayj->first, lons, lats, rownames, true); 125 this_way << "-" << std::to_string (wayj->first); 126 if (last_node >= 0) 127 { 128 first_node = last_node; 129 itr_rw = itw; 130 ptr_check = true; 131 break; 132 } 133 } 134 } // end for itw over relation_ways 135 if (ptr_check) 136 relation_ways.erase (itr_rw); 137 else 138 { 139 // not all OSM multipolygons join up 140 way_okay = false; 141 break; 142 //throw std::runtime_error ("pointer not assigned"); 143 } 144 if (last_node == node0 || relation_ways.size () == 0) 145 closed = true; 146 } // end while !closed 147 if (way_okay && last_node == node0) 148 { 149 lon_vec.push_back (lons); 150 lat_vec.push_back (lats); 151 rowname_vec.push_back (rownames); 152 wayname_vec.push_back (this_way.str ()); 153 ids.push_back (this_way.str ()); 154 } 155 lats.clear (); // These can't be reserved here 156 lons.clear (); 157 rownames.clear (); 158 } // end while relation_ways.size == 0 - finished tracing relation 159 wayname_vec.clear (); 160} 161 162 163/* Traces a single multilinestring relation 164 * 165 * 166 * GDAL does a simple dump of all ways as one multistring. osmdata creates one 167 * multistring for each separate relation role, resulting in as many multistrings 168 * as there are roles. 169 * 170 * @param itr_rel iterator to XmlData::Relations structure 171 * @param role trace ways only matching this role in the relation 172 * @param &ways pointer to Ways structure 173 * @param &nodes pointer to Nodes structure 174 * @param &lon_vec pointer to 2D array of longitudes 175 * @param &lat_vec pointer to 2D array of latitudes 176 * @param &rowname_vec pointer to 2D array of rownames for each node. 177 * @param &id_vec pointer to 2D array of OSM IDs for each way in relation 178 */ 179void trace_multilinestring (Relations::const_iterator &itr_rel, 180 const std::string role, const Ways &ways, const Nodes &nodes, 181 double_arr2 &lon_vec, double_arr2 &lat_vec, string_arr2 &rowname_vec, 182 std::vector <osmid_t> &ids) 183{ 184 std::vector <double> lons, lats; 185 std::vector <std::string> rownames; 186 187 osm_str_vec relation_ways; 188 //relation_ways.reserve (itr_rel->ways.size ()); 189 for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) 190 if (itw->second == role) 191 relation_ways.push_back (std::make_pair (itw->first, itw->second)); 192 193 // Then trace through all those relations and store associated data 194 while (relation_ways.size () > 0) 195 { 196 auto rwi = relation_ways.begin (); 197 ids.push_back (rwi->first); 198 auto wayi = ways.find (rwi->first); 199 //if (wayi == ways.end ()) 200 // throw std::runtime_error ("way can not be found"); 201 // Non-overpass OSM data sets can have way IDs in old changelogs that no 202 // longer exist; this clause ensures that they are simply skipped but 203 // reading continues. Thanks @RobinLovelace 204 if (wayi != ways.end ()) 205 { 206 osmid_t first_node = wayi->second.nodes.front (); 207 first_node = trace_way (ways, nodes, first_node, 208 wayi->first, lons, lats, rownames, false); 209 210 lon_vec.push_back (lons); 211 lat_vec.push_back (lats); 212 rowname_vec.push_back (rownames); 213 214 lons.clear (); 215 lats.clear (); 216 rownames.clear (); 217 } 218 relation_ways.erase (rwi); 219 } // end while relation_ways.size > 0 220} 221 222 223/* trace_way 224 * 225 * Traces a single way and adds (lon,lat,rownames) to corresponding vectors. 226 * This is used only for tracing ways in OSM relations. Direct tracing of ways 227 * stored as 'LINESTRING' or 'POLYGON' objects is done with 'trace_way_nmat ()', 228 * which dumps the results directly to an 'Rcpp::NumericMatrix'. 229 * 230 * @param &ways pointer to Ways structure 231 * @param &nodes pointer to Nodes structure 232 * @param first_node Last node of previous way to find in current 233 * @param &wayi_id pointer to ID of current way 234 * @lons pointer to vector of longitudes 235 * @lats pointer to vector of latitudes 236 * @rownames pointer to vector of rownames for each node. 237 * 238 * @returnn ID of final node in way, or a negative number if first_node does not 239 * within wayi_id 240 */ 241osmid_t trace_way (const Ways &ways, const Nodes &nodes, osmid_t first_node, 242 const osmid_t &wayi_id, std::vector <double> &lons, 243 std::vector <double> &lats, std::vector <std::string> &rownames, 244 const bool append) 245{ 246 osmid_t last_node = -1; 247 auto wayi = ways.find (wayi_id); 248 bool add_node = true; 249 if (append) 250 add_node = false; 251 252 // Alternative to the following is to pass iterators as .begin() or 253 // .rbegin() to a std::for_each, but const Ways and Nodes cannot then 254 // (easily) be passed to lambdas. TODO: Find a way 255 if (first_node < 0 || wayi->second.nodes.front () == first_node) 256 { 257 for (auto ni = wayi->second.nodes.begin (); 258 ni != wayi->second.nodes.end (); ++ni) 259 { 260 if (nodes.find (*ni) == nodes.end ()) 261 throw std::runtime_error ("node can not be found"); 262 if (!add_node) 263 add_node = true; 264 else 265 { 266 lons.push_back (nodes.find (*ni)->second.lon); 267 lats.push_back (nodes.find (*ni)->second.lat); 268 rownames.push_back (std::to_string (*ni)); 269 } 270 } 271 last_node = wayi->second.nodes.back (); 272 } else if (wayi->second.nodes.back () == first_node) 273 { 274 for (auto ni = wayi->second.nodes.rbegin (); 275 ni != wayi->second.nodes.rend (); ++ni) 276 { 277 if (nodes.find (*ni) == nodes.end ()) 278 throw std::runtime_error ("node can not be found"); 279 if (!add_node) 280 add_node = true; 281 else 282 { 283 lons.push_back (nodes.find (*ni)->second.lon); 284 lats.push_back (nodes.find (*ni)->second.lat); 285 rownames.push_back (std::to_string (*ni)); 286 } 287 } 288 last_node = wayi->second.nodes.front (); 289 } 290 291 return last_node; 292}