/***************************************************************************
* Project: osmdata
* File: osmdatap-sc.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
* E-Mail: mark.padgham@email.com
*
* Description: Silicate (SC) parsing of OSM XML file
*
* 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"
std::string random_id (size_t len);
/************************************************************************
************************************************************************
** **
** CLASS::XMLDATASC **
** **
************************************************************************
************************************************************************/
class XmlDataSC
{
/* Two main options to efficiently store-on-reading are:
* 1. Use std::maps for everything, but this would ultimately require
* copying all entries over to an appropriate Rcpp::Matrix class; or
* 2. Setting up individual vectors for each (id, key, val), and just
* Rcpp::wrap-ing them for return.
* The second is more efficient, and so is implemented here, via an initial
* read to determine the sizes of the vectors (in Counters), then a second
* read to store them.
*/
public:
struct Counters {
// Initial function getSizes does an initial scan of the XML doc and
// establishes the sizes of everything with these counters
size_t nnodes, nnode_kv,
nways, nway_kv, nedges,
nrels, nrel_kv, nrel_memb;
std::string id;
};
struct Vectors {
// Vectors used to store the data, with sizes allocated according to
// the values of Counters
//
// vectors for key-val pairs in object table:
std::vector
rel_kv_id, rel_key, rel_val,
rel_memb_id, rel_memb_type, rel_ref, rel_role,
way_id, way_key, way_val,
node_id, node_key, node_val;
// vectors for edge and object_link_edge tables:
std::vector vx0, vx1, edge, object;
// vectors for vertex table
std::vector vx, vy;
std::vector vert_id;
};
struct Maps {
std::unordered_map >
rel_membs, way_membs;
};
private:
Counters counters;
Vectors vectors;
Maps maps;
// Number of nodes in each way, and ways in each rel
std::unordered_map waySizes, relSizes;
public:
XmlDataSC (const std::string& str)
{
// APS empty m_nodes/m_ways/m_relations constructed here, no need to explicitly clear
XmlDocPtr p = parseXML (str);
zeroCounters ();
getSizes (p->first_node ());
vectorsResize ();
zeroCounters ();
traverseWays (p->first_node ());
}
// APS make the dtor virtual since compiler support for "final" is limited
virtual ~XmlDataSC ()
{
}
const std::vector & get_rel_kv_id() const { return vectors.rel_kv_id; }
const std::vector & get_rel_key() const { return vectors.rel_key; }
const std::vector & get_rel_val() const { return vectors.rel_val; }
const std::vector & get_rel_memb_id() const { return vectors.rel_memb_id; }
const std::vector & get_rel_memb_type() const { return vectors.rel_memb_type; }
const std::vector & get_rel_ref() const { return vectors.rel_ref; }
const std::vector & get_rel_role() const { return vectors.rel_role; }
const std::vector & get_way_id() const { return vectors.way_id; }
const std::vector & get_way_key() const { return vectors.way_key; }
const std::vector & get_way_val() const { return vectors.way_val; }
const std::vector & get_node_id() const { return vectors.node_id; }
const std::vector & get_node_key() const { return vectors.node_key; }
const std::vector & get_node_val() const { return vectors.node_val; }
// vectors for edge and object_link_edge tables:
const std::vector & get_vx0 () const { return vectors.vx0; }
const std::vector & get_vx1 () const { return vectors.vx1; }
const std::vector & get_edge () const { return vectors.edge; }
const std::vector & get_object () const { return vectors.object; }
// vectors for vertex table
const std::vector & get_vert_id () const { return vectors.vert_id; }
const std::vector & get_vx () const { return vectors.vx; }
const std::vector & get_vy () const { return vectors.vy; }
const std::unordered_map >&
get_rel_membs () const { return maps.rel_membs; }
const std::unordered_map >&
get_way_membs () const { return maps.way_membs; }
private:
void zeroCounters ();
void getSizes (XmlNodePtr pt);
void vectorsResize ();
void countRelation (XmlNodePtr pt);
void countWay (XmlNodePtr pt);
void countNode (XmlNodePtr pt);
void traverseWays (XmlNodePtr pt); // The primary function
void traverseRelation (XmlNodePtr pt, size_t &memb_num);
void traverseWay (XmlNodePtr pt, size_t& node_num);
void traverseNode (XmlNodePtr pt);
}; // end Class::XmlDataSC
inline void XmlDataSC::zeroCounters ()
{
counters.nnodes = 0;
counters.nnode_kv = 0;
counters.nways = 0;
counters.nway_kv = 0;
counters.nedges = 0;
counters.nrels = 0;
counters.nrel_kv = 0;
counters.nrel_memb = 0;
}
inline void XmlDataSC::vectorsResize ()
{
vectors.rel_kv_id.resize (counters.nrel_kv);
vectors.rel_key.resize (counters.nrel_kv);
vectors.rel_val.resize (counters.nrel_kv);
vectors.rel_memb_id.resize (counters.nrel_memb);
vectors.rel_memb_type.resize (counters.nrel_memb);
vectors.rel_ref.resize (counters.nrel_memb);
vectors.rel_role.resize (counters.nrel_memb);
vectors.way_id.resize (counters.nway_kv);
vectors.way_key.resize (counters.nway_kv);
vectors.way_val.resize (counters.nway_kv);
vectors.node_id.resize (counters.nnode_kv);
vectors.node_key.resize (counters.nnode_kv);
vectors.node_val.resize (counters.nnode_kv);
vectors.vx0.resize (counters.nedges);
vectors.vx1.resize (counters.nedges);
vectors.edge.resize (counters.nedges);
vectors.object.resize (counters.nedges);
vectors.vx.resize (counters.nnodes);
vectors.vy.resize (counters.nnodes);
vectors.vert_id.resize (counters.nnodes);
for (auto m: relSizes)
{
maps.rel_membs.emplace (m.first, std::vector (m.second));
}
for (auto m: waySizes)
{
maps.way_membs.emplace (m.first, std::vector (m.second));
}
}
/************************************************************************
************************************************************************
** **
** FUNCTION::GETSIZES **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::getSizes (XmlNodePtr pt)
{
for (XmlNodePtr it = pt->first_node (); it != nullptr;
it = it->next_sibling())
{
if (!strcmp (it->name(), "node"))
{
countNode (it); // increments nnode_kv
counters.nnodes++;
}
else if (!strcmp (it->name(), "way"))
{
size_t wayLength = counters.nedges;
countWay (it); // increments nway_kv, nedges
wayLength = counters.nedges - wayLength;
counters.nedges--; // counts nodes, so each way has nedges = 1 - nnodes
waySizes.emplace (counters.id, wayLength);
counters.nways++;
}
else if (!strcmp (it->name(), "relation"))
{
size_t relLength = counters.nrel_memb;
countRelation (it); // increments nrel_kv, nrel_memb
relLength = counters.nrel_memb - relLength;
relSizes.emplace (counters.id, relLength);
counters.nrels++;
}
else
{
getSizes (it);
}
}
} // end function XmlDataSC::getSizes
/************************************************************************
************************************************************************
** **
** FUNCTION::COUNTRELATION **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::countRelation (XmlNodePtr pt)
{
// Relations can have either members or key-val pairs, counted here with
// separate counters
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
counters.id = it->value();
else if (!strcmp (it->name(), "type"))
counters.nrel_memb++;
else if (!strcmp (it->name(), "k"))
counters.nrel_kv++;
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
countRelation (it);
}
} // end function XmlDataSC::countRelation
/************************************************************************
************************************************************************
** **
** FUNCTION::COUNTWAY **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::countWay (XmlNodePtr pt)
{
// Ways can have either member nodes, called "ref", or key-val pairs
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
counters.id = it->value();
else if (!strcmp (it->name(), "k"))
counters.nway_kv++;
else if (!strcmp (it->name(), "ref"))
counters.nedges++;
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
countWay (it);
}
} // end function XmlDataSC::countWay
/************************************************************************
************************************************************************
** **
** FUNCTION::COUNTNODE **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::countNode (XmlNodePtr pt)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "k"))
counters.nnode_kv++;
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
countNode (it);
}
} // end function XmlDataSC::countNode
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSEWAYS **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::traverseWays (XmlNodePtr pt)
{
for (XmlNodePtr it = pt->first_node (); it != nullptr;
it = it->next_sibling())
{
if (!strcmp (it->name(), "node"))
{
traverseNode (it);
counters.nnodes++;
} else if (!strcmp (it->name(), "way"))
{
size_t node_num = 0;
traverseWay (it, node_num);
counters.nways++;
}
else if (!strcmp (it->name(), "relation"))
{
size_t memb_num = 0;
traverseRelation (it, memb_num);
counters.nrels++;
}
else
{
traverseWays (it);
}
}
} // end function XmlDataSC::traverseWays
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSERELATION **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::traverseRelation (XmlNodePtr pt, size_t &memb_num)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
{
// These values are always first, so all other clauses are executed
// after this one
counters.id = it->value();
} else if (!strcmp (it->name(), "k"))
{
vectors.rel_kv_id [counters.nrel_kv] = counters.id;
vectors.rel_key [counters.nrel_kv] = it->value();
} else if (!strcmp (it->name(), "v"))
vectors.rel_val [counters.nrel_kv++] = it->value();
else if (!strcmp (it->name(), "type"))
{
vectors.rel_memb_type [counters.nrel_memb] = it->value();
vectors.rel_memb_id [counters.nrel_memb] = counters.id;
} else if (!strcmp (it->name(), "ref"))
{
vectors.rel_ref [counters.nrel_memb] = it->value();
// TODO: Is there a safer alternative to next line?
maps.rel_membs.at (counters.id) [memb_num++] = it->value();
} else if (!strcmp (it->name(), "role"))
vectors.rel_role [counters.nrel_memb++] = it->value();
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseRelation (it, memb_num);
}
} // end function XmlDataSC::traverseRelation
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSEWAY **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::traverseWay (XmlNodePtr pt, size_t& node_num)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
{
// These values are always first, so all other clauses are executed
// after this one
counters.id = it->value();
} else if (!strcmp (it->name(), "k"))
{
vectors.way_id [counters.nway_kv] = counters.id;
vectors.way_key [counters.nway_kv] = it->value();
} else if (!strcmp (it->name(), "v"))
vectors.way_val [counters.nway_kv++] = it->value();
else if (!strcmp (it->name(), "ref"))
{
maps.way_membs.at (counters.id) [node_num] = it->value();
if (node_num == 0)
vectors.vx0 [counters.nedges] = it->value();
else
{
vectors.vx1 [counters.nedges] = it->value();
vectors.object [counters.nedges] = counters.id;
vectors.edge [counters.nedges] = random_id (10);
counters.nedges++;
if (counters.nedges < vectors.vx0.size ())
{
vectors.vx0 [counters.nedges] = it->value();
}
}
node_num++;
}
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseWay (it, node_num);
}
} // end function XmlDataSC::traverseWay
/************************************************************************
************************************************************************
** **
** FUNCTION::TRAVERSENODE **
** **
************************************************************************
************************************************************************/
inline void XmlDataSC::traverseNode (XmlNodePtr pt)
{
for (XmlAttrPtr it = pt->first_attribute (); it != nullptr;
it = it->next_attribute())
{
if (!strcmp (it->name(), "id"))
vectors.vert_id [counters.nnodes] = it->value();
else if (!strcmp (it->name(), "lat"))
vectors.vy [counters.nnodes] = std::stod(it->value());
else if (!strcmp (it->name(), "lon"))
vectors.vx [counters.nnodes] = std::stod(it->value());
else if (!strcmp (it->name(), "k"))
vectors.node_key [counters.nnode_kv] = it->value();
else if (!strcmp (it->name(), "v"))
{
vectors.node_val [counters.nnode_kv] = it->value();
vectors.node_id [counters.nnode_kv] =
vectors.vert_id [counters.nnodes]; // will always be pre-set
counters.nnode_kv++;
}
}
// allows for >1 child nodes
for (XmlNodePtr it = pt->first_node(); it != nullptr; it = it->next_sibling())
{
traverseNode (it);
}
} // end function XmlDataSC::traverseNode
/************************************************************************
************************************************************************
** **
** ADDITIONAL FUNCTIONS **
** **
************************************************************************
************************************************************************/
Rcpp::List rel_membs_as_list (XmlDataSC &xml);
Rcpp::List way_membs_as_list (XmlDataSC &xml);