5 #include <boost/format.hpp>
11 using LongLong =
long long;
14 constexpr
const char* Osm =
"osm";
15 constexpr
const char* Tag =
"tag";
16 constexpr
const char* Key =
"k";
17 constexpr
const char* Value =
"v";
18 constexpr
const char*
Node =
"node";
19 constexpr
const char*
Way =
"way";
20 constexpr
const char*
Relation =
"relation";
21 constexpr
const char* Member =
"member";
22 constexpr
const char*
Role =
"role";
23 constexpr
const char*
Type =
"type";
24 constexpr
const char* Nd =
"nd";
25 constexpr
const char* Ref =
"ref";
26 constexpr
const char*
Id =
"id";
27 constexpr
const char* Lat =
"lat";
28 constexpr
const char* Lon =
"lon";
29 constexpr
const char* Version =
"version";
30 constexpr
const char* Visible =
"visible";
31 constexpr
const char* Elevation =
"ele";
32 constexpr
const char* Action =
"action";
33 constexpr
const char* Delete =
"delete";
36 struct UnresolvedRole {
44 for (
auto tag = node.child(keyword::Tag); tag;
45 tag = tag.next_sibling(keyword::Tag)) {
46 if (std::string(tag.attribute(keyword::Key).value()) == keyword::Elevation) {
49 attributes[tag.attribute(keyword::Key).value()] = tag.attribute(keyword::Value).value();
54 bool isDeleted(
const pugi::xml_node& node) {
55 auto action = node.attribute(keyword::Action);
56 return action && std::string(action.value()) == keyword::Delete;
59 std::string toJosmStyle(
const double d,
const bool josm_format_elevation =
false) {
60 std::string str = boost::str(boost::format{josm_format_elevation ?
"%.2f" :
"%.11f"} %
d);
61 str.erase(str.find_last_not_of(
'0') + 1, std::string::npos);
62 str.erase(str.find_last_not_of(
'.') + 1, std::string::npos);
66 void removeAndFixPlaceholders(Primitive** toRemove,
Roles& fromRoles, std::vector<UnresolvedRole>& placeholders) {
69 std::find_if(fromRoles.begin(), fromRoles.end(), [&](
const Role& role) { return &role.second == toRemove; });
70 assert(remIt != fromRoles.end());
71 std::vector<std::pair<size_t, Primitive**>> placeholderLocations;
72 for (
auto it = fromRoles.begin(); it != fromRoles.end(); ++it) {
73 if (it->second ==
nullptr && remIt != it) {
74 auto idx = std::distance(fromRoles.begin(), it);
75 placeholderLocations.emplace_back(it > remIt ? idx - 1 : idx, &it->second);
78 fromRoles.erase(remIt);
79 if (placeholderLocations.empty()) {
83 std::map<Primitive**, Primitive**> newLocations;
84 for (
auto& loc : placeholderLocations) {
85 newLocations.emplace(loc.second, &std::next(fromRoles.begin(),
long(loc.first))->second);
88 for (
auto& placeholder : placeholders) {
89 auto it = newLocations.find(placeholder.location);
90 if (it != newLocations.end()) {
91 placeholder.location = it->second;
99 auto xml = std::make_unique<pugi::xml_document>();
100 auto osmNode = xml->append_child(keyword::Osm);
101 osmNode.append_attribute(
"version") =
"0.6";
104 const auto iter = params.find(
"josm_upload");
105 if (iter != params.end() && iter->second.asBool().value_or(
false)) {
106 osmNode.append_attribute(
"upload") =
"true";
108 osmNode.append_attribute(
"upload") =
"false";
111 osmNode.append_attribute(
"generator") =
"lanelet2";
114 const auto iter = params.find(
"josm_format_elevation");
115 const bool josm_format_elevation = iter != params.end() && iter->second.asBool().value_or(
false);
116 lanelet::osm::OsmFileWriter::writeNodes(osmNode, osmFile.nodes, josm_format_elevation);
117 lanelet::osm::OsmFileWriter::writeWays(osmNode, osmFile.ways);
118 lanelet::osm::OsmFileWriter::writeRelations(osmNode, osmFile.relations);
123 static void writeAttributes(pugi::xml_node& elemNode,
const Attributes& attributes) {
124 for (
const auto& attribute : attributes) {
125 auto tagNode = elemNode.append_child(keyword::Tag);
126 tagNode.append_attribute(keyword::Key) = attribute.first.c_str();
127 tagNode.append_attribute(keyword::Value) = attribute.second.c_str();
131 static void writeNodes(pugi::xml_node& osmNode,
const Nodes& nodes,
const bool josm_format_elevation =
false) {
132 for (
const auto& node : nodes) {
133 auto xmlNode = osmNode.append_child(keyword::Node);
134 xmlNode.append_attribute(keyword::Id) = LongLong(node.second.id);
135 if (node.second.id > 0) {
136 xmlNode.append_attribute(keyword::Visible) =
"true";
137 xmlNode.append_attribute(keyword::Version) = 1;
139 xmlNode.append_attribute(keyword::Lat) = toJosmStyle(node.second.point.lat).c_str();
140 xmlNode.append_attribute(keyword::Lon) = toJosmStyle(node.second.point.lon).c_str();
142 if (node.second.point.ele != 0.) {
143 auto tagNode = xmlNode.append_child(keyword::Tag);
144 tagNode.append_attribute(keyword::Key) = keyword::Elevation;
145 tagNode.append_attribute(keyword::Value) = toJosmStyle(node.second.point.ele, josm_format_elevation).c_str();
147 writeAttributes(xmlNode, node.second.attributes);
151 static void writeWays(pugi::xml_node& osmNode,
const Ways& ways) {
152 for (
const auto& wayElem : ways) {
153 const auto& way = wayElem.second;
154 auto xmlNode = osmNode.append_child(keyword::Way);
155 xmlNode.append_attribute(keyword::Id) = LongLong(way.id);
157 xmlNode.append_attribute(keyword::Visible) =
"true";
158 xmlNode.append_attribute(keyword::Version) = 1;
160 for (
const auto& node : way.nodes) {
161 auto nd = xmlNode.append_child(keyword::Nd);
162 nd.append_attribute(keyword::Ref) = LongLong(node->id);
164 writeAttributes(xmlNode, way.attributes);
168 static void writeRelations(pugi::xml_node& osmNode,
const Relations& relations) {
169 for (
const auto& relationElem : relations) {
170 const auto&
relation = relationElem.second;
171 auto xmlNode = osmNode.append_child(keyword::Relation);
172 xmlNode.append_attribute(keyword::Id) = LongLong(
relation.id);
174 xmlNode.append_attribute(keyword::Visible) =
"true";
175 xmlNode.append_attribute(keyword::Version) = 1;
177 for (
const auto& role :
relation.members) {
178 auto xmlMember = xmlNode.append_child(keyword::Member);
179 auto type = role.second->type();
180 xmlMember.append_attribute(keyword::Type) = type.c_str();
181 xmlMember.append_attribute(keyword::Ref) = LongLong(role.second->id);
182 xmlMember.append_attribute(
keyword::Role) = role.first.c_str();
184 writeAttributes(xmlNode,
relation.attributes);
189 class OsmFileParser {
191 static File
read(
const pugi::xml_node& fileNode,
Errors* errors =
nullptr) {
192 OsmFileParser osmParser;
194 auto osmNode = fileNode.child(keyword::Osm);
195 file.nodes = lanelet::osm::OsmFileParser::readNodes(osmNode);
196 file.ways = osmParser.readWays(osmNode,
file.nodes);
197 file.relations = osmParser.readRelations(osmNode,
file.nodes,
file.ways);
198 if (errors !=
nullptr) {
199 *errors = osmParser.errors_;
205 static Nodes readNodes(
const pugi::xml_node& osmNode) {
207 for (
auto node = osmNode.child(keyword::Node); node;
208 node = node.next_sibling(keyword::Node)) {
209 if (isDeleted(node)) {
212 const auto id = node.attribute(keyword::Id).as_llong(
InvalId);
213 const auto attributes = tags(node);
214 const auto lat = node.attribute(keyword::Lat).as_double(0.);
215 const auto lon = node.attribute(keyword::Lon).as_double(0.);
216 const auto ele = node.find_child_by_attribute(keyword::Tag, keyword::Key, keyword::Elevation)
217 .attribute(keyword::Value)
219 nodes[
id] = Node{
id, attributes, {lat, lon, ele}};
224 Ways readWays(
const pugi::xml_node& osmNode,
Nodes& nodes) {
226 for (
auto node = osmNode.child(keyword::Way); node;
227 node = node.next_sibling(keyword::Way)) {
228 if (isDeleted(node)) {
231 const auto id = node.attribute(keyword::Id).as_llong(
InvalId);
232 const auto attributes = tags(node);
233 const auto nodeIds = [&node] {
235 for (
auto refNode = node.child(keyword::Nd); refNode;
236 refNode = refNode.next_sibling(keyword::Nd)) {
237 ids.push_back(refNode.attribute(keyword::Ref).as_llong());
241 std::vector<Node*> wayNodes;
243 wayNodes =
utils::transform(nodeIds, [&nodes](
const auto& elem) {
return &nodes.at(elem); });
244 }
catch (std::out_of_range&) {
245 reportParseError(
id,
"Way references nonexisting points");
247 ways[
id] = Way{
id, attributes, wayNodes};
256 std::vector<UnresolvedRole> unresolvedRoles;
257 for (
auto node = osmNode.child(keyword::Relation); node;
258 node = node.next_sibling(keyword::Relation)) {
259 if (isDeleted(node)) {
262 const auto id = node.attribute(keyword::Id).as_llong(
InvalId);
263 const auto attributes = tags(node);
264 auto&
relation = relations.emplace(
id, Relation{
id, attributes, {}}).first->second;
268 for (
auto member = node.child(keyword::Member); member;
269 member = member.next_sibling(keyword::Member)) {
270 Id memberId = member.attribute(keyword::Ref).as_llong();
271 const std::string role = member.attribute(
keyword::Role).value();
272 const std::string type = member.attribute(keyword::Type).value();
274 if (type == keyword::Node) {
275 roles.emplace_back(role, &nodes.at(memberId));
276 }
else if (type == keyword::Way) {
277 roles.emplace_back(role, &ways.at(memberId));
278 }
else if (type == keyword::Relation) {
280 roles.emplace_back(role,
nullptr);
281 unresolvedRoles.push_back(UnresolvedRole{
id, memberId, &roles.back().second});
283 }
catch (std::out_of_range&) {
284 reportParseError(
id,
"Relation has nonexistent member " + std::to_string(memberId));
290 for (
const auto& unresolvedRole : unresolvedRoles) {
292 assert(*unresolvedRole.location ==
nullptr);
293 *unresolvedRole.location = &relations.at(unresolvedRole.referencedRelation);
294 }
catch (std::out_of_range&) {
295 reportParseError(unresolvedRole.relation,
"Relation references nonexistent relation " +
296 std::to_string(unresolvedRole.referencedRelation));
299 auto&
relation = relations.at(unresolvedRole.relation);
300 removeAndFixPlaceholders(unresolvedRole.location,
relation.members, unresolvedRoles);
306 OsmFileParser() =
default;
307 void reportParseError(
Id id,
const std::string& what) {
308 auto errstr =
"Error reading primitive with id " + std::to_string(
id) +
" from file: " + what;
316 auto nodesEqual = [&lhs, &rhs]() {
317 for (
auto i = 0u; i < lhs.
nodes.size(); i++) {
324 return lhs.
id == rhs.
id && lhs.
nodes.size() == rhs.
nodes.size() && nodesEqual();
328 auto membersEqual = [&lhs, &rhs]() {
329 for (
auto itL = lhs.
members.begin(), itR = rhs.
members.begin(); itL != lhs.
members.end(); ++itL, ++itR) {
330 if (itL->second->type() != itR->second->type() && itL->second->id != itR->second->id) {
336 return lhs.
id == rhs.
id && lhs.
members.size() == rhs.
members.size() && membersEqual();