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