/*************************************************************************** * Project: osmdata * File: trace-osm.cpp * Language: C++ * * osmdata is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * osmdata is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * osm-router. If not, see . * * Author: Mark Padgham * E-Mail: mark.padgham@email.com * * Description: Functions to trace OSM ways and store in C++ dynamic arrays * (no RCpp here). * * Limitations: * * Dependencies: none (rapidXML header included in osmdata) * * Compiler Options: -std=c++11 ***************************************************************************/ #include "trace-osm.h" /* Traces a single relation of any type (SC only) * * @param itr_rel iterator to XmlData::Relations structure */ void trace_relation (Relations::const_iterator &itr_rel, osm_str_vec &relation_ways, std::vector > & relation_kv) { relation_ways.reserve (itr_rel->ways.size ()); for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) relation_ways.push_back (std::make_pair (itw->first, itw->second)); relation_kv.reserve (itr_rel->key_val.size ()); for (auto itk = itr_rel->key_val.begin (); itk != itr_rel->key_val.end (); ++itk) relation_kv.push_back (std::make_pair (itk->first, itk->second)); } /* Traces a single multipolygon relation * * @param itr_rel iterator to XmlData::Relations structure * @param &ways pointer to Ways structure * @param &nodes pointer to Nodes structure * @param &lon_vec pointer to 2D array of longitudes * @param &lat_vec pointer to 2D array of latitudes * @param &rowname_vec pointer to 2D array of rownames for each node. * @param &id_vec pointer to 2D array of OSM IDs for each way in relation */ void trace_multipolygon (Relations::const_iterator &itr_rel, const Ways &ways, const Nodes &nodes, double_arr2 &lon_vec, double_arr2 &lat_vec, string_arr2 &rowname_vec, std::vector &ids) { bool closed, ptr_check; osmid_t node0, first_node, last_node; std::string this_role; std::stringstream this_way; std::vector lons, lats; std::vector rownames, wayname_vec; osm_str_vec relation_ways; relation_ways.reserve (itr_rel->ways.size ()); for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) relation_ways.push_back (std::make_pair (itw->first, itw->second)); it_osm_str_vec itr_rw; bool way_okay = true; // Then trace through all those relations and store associated data while (relation_ways.size () > 0) { auto rwi = relation_ways.begin (); if (rwi->second != "outer") // "outer" role first { while (std::distance (rwi, relation_ways.end ()) > 1) { std::advance (rwi, 1); if (rwi->second == "outer") break; } if (rwi->second != "outer") // just reset to first rwi = relation_ways.begin (); } this_role = rwi->second; auto wayi = ways.find (rwi->first); if (wayi == ways.end ()) throw std::runtime_error ("way can not be found"); this_way.str (""); this_way << std::to_string (rwi->first); relation_ways.erase (rwi); // Get first way of relation, and starting node node0 = wayi->second.nodes.front (); last_node = trace_way (ways, nodes, node0, wayi->first, lons, lats, rownames, false); closed = false; if (last_node == node0) closed = true; while (!closed) { first_node = last_node; ptr_check = false; for (auto itw = relation_ways.begin (); itw != relation_ways.end (); ++itw) { if (itw->second == this_role) { auto wayj = ways.find (itw->first); if (wayj == ways.end ()) throw std::runtime_error ("way can not be found"); last_node = trace_way (ways, nodes, first_node, wayj->first, lons, lats, rownames, true); this_way << "-" << std::to_string (wayj->first); if (last_node >= 0) { first_node = last_node; itr_rw = itw; ptr_check = true; break; } } } // end for itw over relation_ways if (ptr_check) relation_ways.erase (itr_rw); else { // not all OSM multipolygons join up way_okay = false; break; //throw std::runtime_error ("pointer not assigned"); } if (last_node == node0 || relation_ways.size () == 0) closed = true; } // end while !closed if (way_okay && last_node == node0) { lon_vec.push_back (lons); lat_vec.push_back (lats); rowname_vec.push_back (rownames); wayname_vec.push_back (this_way.str ()); ids.push_back (this_way.str ()); } lats.clear (); // These can't be reserved here lons.clear (); rownames.clear (); } // end while relation_ways.size == 0 - finished tracing relation wayname_vec.clear (); } /* Traces a single multilinestring relation * * * GDAL does a simple dump of all ways as one multistring. osmdata creates one * multistring for each separate relation role, resulting in as many multistrings * as there are roles. * * @param itr_rel iterator to XmlData::Relations structure * @param role trace ways only matching this role in the relation * @param &ways pointer to Ways structure * @param &nodes pointer to Nodes structure * @param &lon_vec pointer to 2D array of longitudes * @param &lat_vec pointer to 2D array of latitudes * @param &rowname_vec pointer to 2D array of rownames for each node. * @param &id_vec pointer to 2D array of OSM IDs for each way in relation */ void trace_multilinestring (Relations::const_iterator &itr_rel, const std::string role, const Ways &ways, const Nodes &nodes, double_arr2 &lon_vec, double_arr2 &lat_vec, string_arr2 &rowname_vec, std::vector &ids) { std::vector lons, lats; std::vector rownames; osm_str_vec relation_ways; //relation_ways.reserve (itr_rel->ways.size ()); for (auto itw = itr_rel->ways.begin (); itw != itr_rel->ways.end (); ++itw) if (itw->second == role) relation_ways.push_back (std::make_pair (itw->first, itw->second)); // Then trace through all those relations and store associated data while (relation_ways.size () > 0) { auto rwi = relation_ways.begin (); ids.push_back (rwi->first); auto wayi = ways.find (rwi->first); //if (wayi == ways.end ()) // throw std::runtime_error ("way can not be found"); // Non-overpass OSM data sets can have way IDs in old changelogs that no // longer exist; this clause ensures that they are simply skipped but // reading continues. Thanks @RobinLovelace if (wayi != ways.end ()) { osmid_t first_node = wayi->second.nodes.front (); first_node = trace_way (ways, nodes, first_node, wayi->first, lons, lats, rownames, false); lon_vec.push_back (lons); lat_vec.push_back (lats); rowname_vec.push_back (rownames); lons.clear (); lats.clear (); rownames.clear (); } relation_ways.erase (rwi); } // end while relation_ways.size > 0 } /* trace_way * * Traces a single way and adds (lon,lat,rownames) to corresponding vectors. * This is used only for tracing ways in OSM relations. Direct tracing of ways * stored as 'LINESTRING' or 'POLYGON' objects is done with 'trace_way_nmat ()', * which dumps the results directly to an 'Rcpp::NumericMatrix'. * * @param &ways pointer to Ways structure * @param &nodes pointer to Nodes structure * @param first_node Last node of previous way to find in current * @param &wayi_id pointer to ID of current way * @lons pointer to vector of longitudes * @lats pointer to vector of latitudes * @rownames pointer to vector of rownames for each node. * * @returnn ID of final node in way, or a negative number if first_node does not * within wayi_id */ osmid_t trace_way (const Ways &ways, const Nodes &nodes, osmid_t first_node, const osmid_t &wayi_id, std::vector &lons, std::vector &lats, std::vector &rownames, const bool append) { osmid_t last_node = -1; auto wayi = ways.find (wayi_id); bool add_node = true; if (append) add_node = false; // Alternative to the following is to pass iterators as .begin() or // .rbegin() to a std::for_each, but const Ways and Nodes cannot then // (easily) be passed to lambdas. TODO: Find a way if (first_node < 0 || wayi->second.nodes.front () == first_node) { for (auto ni = wayi->second.nodes.begin (); ni != wayi->second.nodes.end (); ++ni) { if (nodes.find (*ni) == nodes.end ()) throw std::runtime_error ("node can not be found"); if (!add_node) add_node = true; else { lons.push_back (nodes.find (*ni)->second.lon); lats.push_back (nodes.find (*ni)->second.lat); rownames.push_back (std::to_string (*ni)); } } last_node = wayi->second.nodes.back (); } else if (wayi->second.nodes.back () == first_node) { for (auto ni = wayi->second.nodes.rbegin (); ni != wayi->second.nodes.rend (); ++ni) { if (nodes.find (*ni) == nodes.end ()) throw std::runtime_error ("node can not be found"); if (!add_node) add_node = true; else { lons.push_back (nodes.find (*ni)->second.lon); lats.push_back (nodes.find (*ni)->second.lat); rownames.push_back (std::to_string (*ni)); } } last_node = wayi->second.nodes.front (); } return last_node; }