/***************************************************************************
* Project: osmdata
* File: osmdata-data_frame.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
* osmdata. If not, see .
*
* Author: Mark Padgham
* E-Mail: mark.padgham@email.com
*
* Description: Modified version of 'osmdata-sf' to extract OSM data from an
* object of class XmlData and return it in Rcpp::List format,
* but in this case ignoring the actual geometric data. The
* returned object contains key-value data only, for return
* from the R function 'osmdata_data_frame'.
*
* Limitations:
*
* Dependencies: none (rapidXML header included in osmdata)
*
* Compiler Options: -std=c++11
***************************************************************************/
#include "osmdata.h"
#include
#include
#include
// Note: This code uses explicit index counters within most loops which use Rcpp
// objects, because these otherwise require a
// static_cast (std::distance (...)). This operation copies each
// instance and can slow the loops down by several orders of magnitude!
/************************************************************************
************************************************************************
** **
** 1. PRIMARY FUNCTIONS TO TRACE WAYS AND RELATIONS **
** **
************************************************************************
************************************************************************/
//' get_osm_relations
//'
//' Return a dual Rcpp::DataFrame containing all OSM relations.
//'
//' @param rels Pointer to the vector of Relation objects
//' @param unique_vals Pointer to a UniqueVals object containing std::sets of
//' all unique IDs and keys for each kind of OSM object (nodes, ways, rels).
//'
//' @return A list with three Rcpp::DataFrame with the tags, centers and
//' metadata of the relations.
//'
//' @noRd
Rcpp::List osm_df::get_osm_relations (const Relations &rels,
const UniqueVals &unique_vals)
{
const unsigned int nmp = static_cast (rels.size ());
Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue);
if (nmp == 0L) {
return res;
}
size_t ncol = unique_vals.k_rel.size ();
std::vector rel_ids;
rel_ids.reserve (nmp);
Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nmp, ncol));
std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING);
Rcpp::CharacterMatrix meta (Rcpp::Dimension (nmp, 5L));
std::fill (meta.begin (), meta.end (), NA_STRING);
Rcpp::NumericMatrix center (Rcpp::Dimension (nmp, 2L));
std::fill (center.begin (), center.end (), NA_REAL);
unsigned int count = 0;
for (auto itr = rels.begin (); itr != rels.end (); ++itr)
{
if (count % 1000 == 0)
Rcpp::checkUserInterrupt ();
rel_ids.push_back (std::to_string (itr->id));
meta (count, 0L) = itr->_version;
meta (count, 1L) = itr->_timestamp;
meta (count, 2L) = itr->_changeset;
meta (count, 3L) = itr->_uid;
meta (count, 4L) = itr->_user;
center (count, 0L) = itr->_lat;
center (count, 1L) = itr->_lon;
osm_convert::get_value_mat_rel (itr, unique_vals, kv_mat, count++);
}
Rcpp::DataFrame kv_df;
kv_mat.attr ("dimnames") = Rcpp::List::create (rel_ids, unique_vals.k_rel);
kv_df = osm_convert::restructure_kv_mat (kv_mat, false);
meta.attr ("dimnames") = Rcpp::List::create (rel_ids, metanames);
center.attr ("dimnames") = Rcpp::List::create (rel_ids, centernames);
res (0) = kv_df;
res (1) = meta;
res (2) = center;
rel_ids.clear ();
return res;
}
//' get_osm_ways
//'
//' Store key-val pairs for OSM ways as a list/data.frame
//'
//' @param kv_df Pointer to Rcpp::DataFrame to hold key-value pairs.
//' @param way_ids Vector of IDs of ways to trace.
//' @param ways Pointer to all ways in data set.
//' @param unique_vals pointer to all unique values (OSM IDs and keys) in data
//' set.
//'
//' @noRd
Rcpp::List osm_df::get_osm_ways (
const std::set &way_ids, const Ways &ways,
const UniqueVals &unique_vals)
{
size_t nrow = way_ids.size (), ncol = unique_vals.k_way.size ();
Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue);
if (nrow == 0L)
{
return res;
}
std::vector waynames;
waynames.reserve (nrow);
Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nrow, ncol));
std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING);
Rcpp::CharacterMatrix meta (Rcpp::Dimension (nrow, 5L));
std::fill (meta.begin (), meta.end (), NA_STRING);
Rcpp::NumericMatrix center (Rcpp::Dimension (nrow, 2L));
std::fill (center.begin (), center.end (), NA_REAL);
unsigned int count = 0;
for (auto wi = way_ids.begin (); wi != way_ids.end (); ++wi)
{
if (count % 1000 == 0)
Rcpp::checkUserInterrupt ();
waynames.push_back (std::to_string (*wi));
auto wj = ways.find (*wi);
meta (count, 0L) = wj->second._version;
meta (count, 1L) = wj->second._timestamp;
meta (count, 2L) = wj->second._changeset;
meta (count, 3L) = wj->second._uid;
meta (count, 4L) = wj->second._user;
center (count, 0L) = wj->second._lat;
center (count, 1L) = wj->second._lon;
osm_convert::get_value_mat_way (wj, unique_vals, kv_mat, count);
count++;
}
Rcpp::DataFrame kv_df = R_NilValue;
kv_mat.attr ("dimnames") = Rcpp::List::create (waynames, unique_vals.k_way);
if (kv_mat.nrow () > 0 && kv_mat.ncol () > 0)
kv_df = osm_convert::restructure_kv_mat (kv_mat, false);
meta.attr ("dimnames") = Rcpp::List::create (waynames, metanames);
center.attr ("dimnames") = Rcpp::List::create (waynames, centernames);
res (0) = kv_df;
res (1) = meta;
res (2) = center;
waynames.clear ();
return res;
}
//' get_osm_nodes
//'
//' Store OSM nodes as `sf::POINT` objects
//'
//' @param kv_df Pointer to Rcpp::DataFrame to hold key-value pairs.
//' @param nodes Pointer to all nodes in data set.
//' @param unique_vals pointer to all unique values (OSM IDs and keys) in data
//' set.
//'
//' @noRd
Rcpp::List osm_df::get_osm_nodes (const Nodes &nodes,
const UniqueVals &unique_vals)
{
size_t nrow = nodes.size (), ncol = unique_vals.k_point.size ();
Rcpp::CharacterMatrix kv_mat (Rcpp::Dimension (nrow, ncol));
std::fill (kv_mat.begin (), kv_mat.end (), NA_STRING);
Rcpp::CharacterMatrix meta (Rcpp::Dimension (nrow, 5L));
std::fill (meta.begin (), meta.end (), NA_STRING);
Rcpp::NumericMatrix center (Rcpp::Dimension (nrow, 2L));
std::fill (center.begin (), center.end (), NA_REAL);
const size_t n = nodes.size ();
std::vector ptnames;
ptnames.reserve (n);
unsigned int count = 0;
for (auto ni = nodes.begin (); ni != nodes.end (); ++ni)
{
if (count % 1000 == 0)
Rcpp::checkUserInterrupt ();
ptnames.push_back (std::to_string (ni->first));
meta (count, 0L) = ni->second._version;
meta (count, 1L) = ni->second._timestamp;
meta (count, 2L) = ni->second._changeset;
meta (count, 3L) = ni->second._uid;
meta (count, 4L) = ni->second._user;
center(count, 0L) = ni->second.lat;
center(count, 1L) = ni->second.lon;
for (auto kv_iter = ni->second.key_val.begin ();
kv_iter != ni->second.key_val.end (); ++kv_iter)
{
const std::string &key = kv_iter->first;
unsigned int ndi = unique_vals.k_point_index.at (key);
kv_mat (count, ndi) = kv_iter->second;
}
count++;
}
Rcpp::DataFrame kv_df = R_NilValue;
Rcpp::List res = Rcpp::List::create (R_NilValue, R_NilValue, R_NilValue);
if (unique_vals.k_point.size () > 0)
{
kv_mat.attr ("dimnames") = Rcpp::List::create (ptnames, unique_vals.k_point);
kv_df = osm_convert::restructure_kv_mat (kv_mat, false);
meta.attr ("dimnames") = Rcpp::List::create (ptnames, metanames);
center.attr ("dimnames") = Rcpp::List::create (ptnames, centernames);
res (0) = kv_df;
res (1) = meta;
res (2) = center;
}
ptnames.clear ();
return res;
}
/************************************************************************
************************************************************************
** **
** THE FINAL RCPP FUNCTION CALLED BY osmdata_df **
** **
************************************************************************
************************************************************************/
//' rcpp_osmdata_df
//'
//' Return OSM data key-value pairs in series of data.frame objects, without any
//' spatial/geometrtic information.
//'
//' @param st Text contents of an overpass API query
//' @return Rcpp::List objects of OSM data
//'
//' @noRd
// [[Rcpp::export]]
Rcpp::List rcpp_osmdata_df (const std::string& st)
{
XmlData xml (st);
const std::map & nodes = xml.nodes ();
const std::map & ways = xml.ways ();
const std::vector & rels = xml.relations ();
const UniqueVals& unique_vals = xml.unique_vals ();
Rcpp::DataFrame kv_rels, kv_df_ways, kv_df_points;
Rcpp::CharacterMatrix meta_rels, meta_ways, meta_nodes;
Rcpp::NumericMatrix center_rels, center_ways, center_nodes;
/* --------------------------------------------------------------
* 1. Extract OSM Relations
* --------------------------------------------------------------*/
Rcpp::List data_rels = osm_df::get_osm_relations (rels, unique_vals);
if (data_rels (0) != R_NilValue)
{
kv_rels = Rcpp::as (data_rels (0));
meta_rels = Rcpp::as (data_rels (1));
center_rels = Rcpp::as (data_rels (2));
}
/* --------------------------------------------------------------
* 2. Extract OSM ways
* --------------------------------------------------------------*/
std::set way_ids;
for (auto itw = ways.begin (); itw != ways.end (); ++itw)
{
way_ids.insert ((*itw).first);
}
Rcpp::List data_ways = osm_df::get_osm_ways (way_ids, ways, unique_vals);
if (data_ways (0) != R_NilValue)
{
kv_df_ways = Rcpp::as (data_ways (0));
meta_ways = Rcpp::as (data_ways (1));
center_ways = Rcpp::as (data_ways (2));
}
/* --------------------------------------------------------------
* 3. Extract OSM nodes
* --------------------------------------------------------------*/
Rcpp::List data_nodes = osm_df::get_osm_nodes (nodes, unique_vals);
if (data_nodes (0) != R_NilValue)
{
kv_df_points = Rcpp::as (data_nodes (0));
meta_nodes = Rcpp::as (data_nodes (1));
center_nodes = Rcpp::as (data_nodes (2));
}
/* --------------------------------------------------------------
* 4. Collate all data
* --------------------------------------------------------------*/
Rcpp::List ret (9);
ret [0] = kv_df_points;
ret [1] = kv_df_ways;
ret [2] = kv_rels;
ret [3] = meta_nodes;
ret [4] = meta_ways;
ret [5] = meta_rels;
ret [6] = center_nodes;
ret [7] = center_ways;
ret [8] = center_rels;
std::vector retnames {"points_kv", "ways_kv", "rels_kv",
"points_meta", "ways_meta", "rels_meta",
"points_center", "ways_center", "rels_center"};
ret.attr ("names") = retnames;
return ret;
}