/***************************************************************************
* Project: osmdata
* File: osmdatap.h
* 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 / Andrew Smith
* E-Mail: mark.padgham@email.com / andrew@casacazaz.net
*
* Description: Class definition of XmlData
*
* Limitations:
*
* Dependencies: none (rapidXML header included in osmdata)
*
* Compiler Options: -std=c++11
***************************************************************************/
#pragma once
#include
#include "common.h"
#include "get-bbox.h"
#include "trace-osm.h"
#include "convert-osm-rcpp.h"
// sf::st_crs(4326)$wkt
const std::string wkt =
"GEOGCRS[\"WGS 84\",\n\
ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n\
MEMBER[\"World Geodetic System 1984 (Transit)\"],\n\
MEMBER[\"World Geodetic System 1984 (G730)\"],\n\
MEMBER[\"World Geodetic System 1984 (G873)\"],\n\
MEMBER[\"World Geodetic System 1984 (G1150)\"],\n\
MEMBER[\"World Geodetic System 1984 (G1674)\"],\n\
MEMBER[\"World Geodetic System 1984 (G1762)\"],\n\
ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n\
LENGTHUNIT[\"metre\",1]],\n\
ENSEMBLEACCURACY[2.0]],\n\
PRIMEM[\"Greenwich\",0,\n\
ANGLEUNIT[\"degree\",0.0174532925199433]],\n\
CS[ellipsoidal,2],\n\
AXIS[\"geodetic latitude (Lat)\",north,\n\
ORDER[1],\n\
ANGLEUNIT[\"degree\",0.0174532925199433]],\n\
AXIS[\"geodetic longitude (Lon)\",east,\n\
ORDER[2],\n\
ANGLEUNIT[\"degree\",0.0174532925199433]],\n\
USAGE[\n\
SCOPE[\"Horizontal component of 3D system.\"],\n\
AREA[\"World.\"],\n\
BBOX[-90,-180,90,180]],\n\
ID[\"EPSG\",4326]]";
const Rcpp::CharacterVector metanames = {"_version", "_timestamp", "_changeset", "_uid", "_user"};
const Rcpp::CharacterVector centernames = {"_lat", "_lon"};
/************************************************************************
************************************************************************
** **
** STRUCTURE OF THESE FILES **
** **
************************************************************************
************************************************************************
*
* 1. osmdata.h = Class definition of XmlData that reads initial XML structure
* 2. trace_osm.h = Primary functions to trace ways and relations (pure C++)
* 2a. trace_multipolygon ()
* 2b. trace_multilinestring ()
* 2c. trace_way ()
* 3. convert_osm_rcpp.h = Functions to convert C++ objects to Rcpp::List objects
* 3a. trace_way_nmat () (here coz it uses Rcpp)
* 3b. get_value_mat_way ()
* 3c. get_value_mat_rel ()
* 3d. convert_poly_linestring_to_Rcpp ()
* 3e. restructure_kv_mat ()
* 4. osmdata.cpp
* 5c. get_osm_relations ()
* 5d. get_osm_ways ()
* 5e. get_osm_nodes ()
* 5a. rcpp_osmdata () - The final Rcpp function called by osmdata_sf
*
* ----------------------------------------------------------------------
*
* The calling hierarchy extends generally from bottom to top as follows:
* rcpp_osmdata () {
* -> get_osm_relations ()
* {
* -> trace_multipolygon ()
* -> trace_way ()
* -> restructure_kv_mat
* -> trace_multilinestring ()
* -> trace_way ()
* -> restructure_kv_mat
* -> get_value_vec ()
* -> convert_poly_linestring_to_Rcpp ()
* -> [... most check and clean functions ...]
* }
* -> get_osm_ways ()
* {
* -> trace_way_nmat ()
* -> get_value_mat_way ()
* -> restructure_kv_mat
* }
* -> get_osm_nodes ()
* -> restructure_kv_mat
* }
*/
/************************************************************************
************************************************************************
** **
** CLASS::XMLDATA **
** **
************************************************************************
************************************************************************/
class XmlData
{
private:
Nodes m_nodes;
Ways m_ways;
Relations m_relations;
UniqueVals m_unique;
public:
double xmin = DOUBLE_MAX, xmax = -DOUBLE_MAX,
ymin = DOUBLE_MAX, ymax = -DOUBLE_MAX;
XmlData (const std::string& str)
{
// APS empty m_nodes/m_ways/m_relations constructed here, no need to explicitly clear
XmlDocPtr p = parseXML (str);
traverseWays (p->first_node ());
make_key_val_indices ();
}
// APS make the dtor virtual since compiler support for "final" is limited
virtual ~XmlData ()
{
// APS m_nodes/m_ways/m_relations destructed here, no need to explicitly clear
}
// Const accessors for members
const Nodes& nodes() const { return m_nodes; }
const Ways& ways() const { return m_ways; }
const Relations& relations() const { return m_relations; }
const UniqueVals& unique_vals() const { return m_unique; }
double x_min() { return xmin; }
double x_max() { return xmax; }
double y_min() { return ymin; }
double y_max() { return ymax; }
private:
void traverseWays (XmlNodePtr pt);
void traverseRelation (XmlNodePtr pt, RawRelation& rrel);
void traverseWay (XmlNodePtr pt, RawWay& rway);
void traverseNode (XmlNodePtr pt, RawNode& rnode);
void make_key_val_indices ();
}; // end Class::XmlData
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSEWAYS **
** **
************************************************************************
************************************************************************/
inline void XmlData::traverseWays (XmlNodePtr pt)
{
RawRelation rrel;
RawWay rway;
Relation relation;
OneWay way;
RawNode rnode;
Node node;
for (XmlNodePtr it = pt->first_node (); it != nullptr;
it = it->next_sibling())
{
if (!strcmp (it->name(), "node"))
{
rnode.key.clear ();
rnode.value.clear ();
traverseNode (it, rnode);
if (rnode.key.size () != rnode.value.size ())
throw std::runtime_error ("sizes of keys and values differ");
// Only insert unique nodes
if (m_unique.id_node.find (rnode.id) == m_unique.id_node.end ())
{
if (rnode.lon < xmin) xmin = rnode.lon;
if (rnode.lon > xmax) xmax = rnode.lon;
if (rnode.lat < ymin) ymin = rnode.lat;
if (rnode.lat > ymax) ymax = rnode.lat;
m_unique.id_node.insert (rnode.id);
node.id = rnode.id;
node.lat = rnode.lat;
node.lon = rnode.lon;
node.key_val.clear ();
for (size_t i=0; iname(), "way"))
{
rway.key.clear ();
rway.value.clear ();
rway.nodes.clear ();
traverseWay (it, rway);
if (rway.key.size () != rway.value.size ())
throw std::runtime_error ("sizes of keys and values differ");
if (m_unique.id_way.find (rway.id) == m_unique.id_way.end ())
{
m_unique.id_way.insert (rway.id);
way.id = rway.id;
way.key_val.clear();
way.nodes.clear();
for (size_t i=0; iname(), "relation"))
{
rrel.key.clear();
rrel.value.clear();
rrel.role_way.clear();
rrel.role_node.clear();
rrel.ways.clear();
rrel.nodes.clear();
rrel.member_type = "";
rrel.ispoly = false;
traverseRelation (it, rrel);
if (rrel.key.size () != rrel.value.size ())
throw std::runtime_error ("sizes of keys and values differ");
if (rrel.ways.size () != rrel.role_way.size ())
throw std::runtime_error ("size of ways and roles differ");
if (rrel.nodes.size () != rrel.role_node.size ())
throw std::runtime_error ("size of nodes and roles differ");
if (m_unique.id_rel.find (rrel.id) == m_unique.id_rel.end ())
{
m_unique.id_rel.insert (rrel.id);
relation.id = rrel.id;
relation.key_val.clear();
relation.ways.clear();
relation.ispoly = rrel.ispoly;
for (size_t i=0; ifirst_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "k"))
rrel.key.push_back (it->value());
else if (!strcmp (it->name(), "v"))
rrel.value.push_back (it->value());
else if (!strcmp (it->name(), "id"))
rrel.id = std::stoll(it->value());
else if (!strcmp (it->name(), "type"))
rrel.member_type = it->value ();
else if (!strcmp (it->name(), "ref"))
{
if (rrel.member_type == "node")
rrel.nodes.push_back (std::stoll (it->value ()));
else if (rrel.member_type == "way")
rrel.ways.push_back (std::stoll (it->value ()));
else if (rrel.member_type == "relation")
rrel.relations.push_back (std::stoll (it->value ()));
else
throw std::runtime_error ("unknown member_type");
} else if (!strcmp (it->name(), "role"))
{
if (rrel.member_type == "node")
rrel.role_node.push_back (it->value ());
else if (rrel.member_type == "way")
rrel.role_way.push_back (it->value ());
else if (rrel.member_type == "relation")
rrel.role_relation.push_back (it->value ());
else
throw std::runtime_error ("unknown member_type");
// Not all OSM Multipolygons have (key="type",
// value="multipolygon"): For example, (key="type",
// value="boundary") are often multipolygons. The things they all
// have are "inner" and "outer" roles.
if (!strcmp (it->value(), "inner") || !strcmp (it->value(), "outer"))
rrel.ispoly = true;
} else if (!strcmp (it->name(), "version"))
rrel._version = it->value();
else if (!strcmp (it->name(), "timestamp"))
rrel._timestamp = it->value();
else if (!strcmp (it->name(), "changeset"))
rrel._changeset = it->value();
else if (!strcmp (it->name(), "uid"))
rrel._uid = it->value();
else if (!strcmp (it->name(), "user"))
rrel._user = it->value();
else if (!strcmp (it->name(), "lat"))
rrel._lat = std::stod(it->value());
else if (!strcmp (it->name(), "lon"))
rrel._lon = std::stod(it->value());
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseRelation (it, rrel);
}
} // end function XmlData::traverseRelation
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSEWAY **
** **
************************************************************************
************************************************************************/
inline void XmlData::traverseWay (XmlNodePtr pt, RawWay& rway)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "k"))
rway.key.push_back (it->value());
else if (!strcmp (it->name(), "v"))
rway.value.push_back (it->value());
else if (!strcmp (it->name(), "id"))
rway.id = std::stoll(it->value());
else if (!strcmp (it->name(), "ref"))
rway.nodes.push_back (std::stoll(it->value()));
else if (!strcmp (it->name(), "version"))
rway._version = it->value();
else if (!strcmp (it->name(), "timestamp"))
rway._timestamp = it->value();
else if (!strcmp (it->name(), "changeset"))
rway._changeset = it->value();
else if (!strcmp (it->name(), "uid"))
rway._uid = it->value();
else if (!strcmp (it->name(), "user"))
rway._user = it->value();
else if (!strcmp (it->name(), "lat"))
rway._lat = std::stod(it->value());
else if (!strcmp (it->name(), "lon"))
rway._lon = std::stod(it->value());
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseWay (it, rway);
}
} // end function XmlData::traverseWay
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSENODE **
** **
************************************************************************
************************************************************************/
inline void XmlData::traverseNode (XmlNodePtr pt, RawNode& rnode)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
rnode.id = std::stoll(it->value());
else if (!strcmp (it->name(), "lat"))
rnode.lat = std::stod(it->value());
else if (!strcmp (it->name(), "lon"))
rnode.lon = std::stod(it->value());
else if (!strcmp (it->name(), "k"))
rnode.key.push_back (it->value ());
else if (!strcmp (it->name(), "v"))
rnode.value.push_back (it->value ());
else if (!strcmp (it->name(), "version")) // metadata
rnode._version = it->value ();
else if (!strcmp (it->name(), "timestamp")) // metadata
rnode._timestamp = it->value ();
else if (!strcmp (it->name(), "changeset")) // metadata
rnode._changeset = it->value ();
else if (!strcmp (it->name(), "uid")) // metadata
rnode._uid = it->value ();
else if (!strcmp (it->name(), "user")) // metadata
rnode._user = it->value ();
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseNode (it, rnode);
}
} // end function XmlData::traverseNode
inline void XmlData::make_key_val_indices ()
{
// These are std::maps which enable keys to be mapped directly onto their
// column number in the key-val matrices
unsigned int i = 0;
for (auto m: m_unique.k_point)
m_unique.k_point_index.insert (std::make_pair (m, i++));
i = 0;
for (auto m: m_unique.k_way)
m_unique.k_way_index.insert (std::make_pair (m, i++));
i = 0;
for (auto m: m_unique.k_rel)
m_unique.k_rel_index.insert (std::make_pair (m, i++));
}
/*---------------------------- fn headers -----------------------------*/
namespace osm_sf {
Rcpp::List get_osm_relations (const Relations &rels,
const std::map &nodes,
const std::map &ways, const UniqueVals &unique_vals,
const Rcpp::NumericVector &bbox, const Rcpp::List &crs);
void get_osm_ways (Rcpp::List &wayList, Rcpp::DataFrame &kv_df,
Rcpp::DataFrame &meta_df,
const std::set &way_ids, const Ways &ways, const Nodes &nodes,
const UniqueVals &unique_vals, const std::string &geom_type,
const Rcpp::NumericVector &bbox, const Rcpp::List &crs);
void get_osm_nodes (Rcpp::List &ptList, Rcpp::DataFrame &kv_df,
Rcpp::DataFrame &meta_df,
const Nodes &nodes, const UniqueVals &unique_vals,
const Rcpp::NumericVector &bbox, const Rcpp::List &crs);
} // end namespace osm_sf
Rcpp::List rcpp_osmdata_sf (const std::string& st);
namespace osm_sp {
void get_osm_nodes (Rcpp::S4 &sp_points, const Nodes &nodes,
const UniqueVals &unique_vals);
void get_osm_ways (Rcpp::S4 &sp_ways,
const std::set &way_ids, const Ways &ways, const Nodes &nodes,
const UniqueVals &unique_vals, const std::string &geom_type);
void get_osm_relations (Rcpp::S4 &multilines, Rcpp::S4 &multipolygons,
const Relations &rels, const std::map &nodes,
const std::map &ways, const UniqueVals &unique_vals);
} // end namespace osm_sp
Rcpp::List rcpp_osmdata_sp (const std::string& st);
namespace osm_sc {
void get_osm_relations (Rcpp::DataFrame &rel_df, Rcpp::DataFrame &kv_df,
const Relations &rels);
void get_osm_ways (Rcpp::DataFrame &edge,
Rcpp::DataFrame &object_link_edge, Rcpp::DataFrame &kv_df,
const Ways &ways);
void get_osm_nodes (Rcpp::DataFrame &node_df, Rcpp::DataFrame &kv_df,
const Nodes &nodes);
} // end namespace osm_sc
Rcpp::List rcpp_osmdata_sc (const std::string& st);
namespace osm_df {
Rcpp::List get_osm_relations (const Relations &rels,
const UniqueVals &unique_vals);
Rcpp::List get_osm_ways (const std::set &way_ids,
const Ways &ways, const UniqueVals &unique_vals);
Rcpp::List get_osm_nodes (const Nodes &nodes,
const UniqueVals &unique_vals);
} // end namespace osm_df
Rcpp::List rcpp_osmdata_df (const std::string& st);