R package for downloading OpenStreetMap data
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}