OsmHandlerWrite.cpp
Go to the documentation of this file.
1 #include <pugixml.hpp>
2 #include <sstream>
3 
8 
9 using namespace std::string_literals;
10 
11 namespace lanelet {
12 namespace io_handlers {
13 
14 using Errors = std::vector<std::string>;
15 namespace {
16 // register with factories
17 RegisterWriter<OsmWriter> regWriter;
18 
19 struct UnresolvedRole {
22  osm::Primitive** location{};
23 };
24 
25 void removeAndFixPlaceholders(osm::Primitive** toRemove, osm::Roles& fromRoles,
26  std::vector<UnresolvedRole>& placeholders) {
27  // find other placeholders that we have to fix
28  auto remIt = std::find_if(fromRoles.begin(), fromRoles.end(), [&](auto& role) { return &role.second == toRemove; });
29  std::vector<std::pair<size_t, osm::Primitive**>> placeholderLocations;
30  for (auto it = fromRoles.begin(); it != fromRoles.end(); ++it) {
31  if (it->second == nullptr && remIt != it) {
32  placeholderLocations.emplace_back(std::distance(fromRoles.begin(), it), &it->second);
33  }
34  }
35  fromRoles.erase(remIt);
36  if (placeholderLocations.empty()) {
37  return; // nothing to update
38  }
39  // get the new locations
40  std::map<osm::Primitive**, osm::Primitive**> newLocations;
41  for (auto& loc : placeholderLocations) {
42  newLocations.emplace(loc.second, &std::next(fromRoles.begin(), long(loc.first))->second);
43  }
44  // adapt existing locations
45  for (auto& placeholder : placeholders) {
46  auto it = newLocations.find(placeholder.location);
47  if (it != newLocations.end()) {
48  placeholder.location = it->second;
49  }
50  }
51 }
52 
53 class ToFileWriter {
54  public:
55  static std::unique_ptr<osm::File> writeMap(const LaneletMap& laneletMap, const Projector& projector,
56  ErrorMessages& errors, const io::Configuration& params) {
57  ToFileWriter writer;
58 
59  writer.writeNodes(laneletMap, projector);
60  writer.writeWays(laneletMap);
61 
62  // we have to wait until lanelets/areas are written
63  auto unparsedLaneletAndAreaParameters = writer.appendRegulatoryElements(laneletMap.regulatoryElementLayer);
64  writer.appendLanelets(laneletMap.laneletLayer);
65  writer.appendAreas(laneletMap.areaLayer);
66  writer.resolveUnparsedMembers(unparsedLaneletAndAreaParameters);
67 
68  writer.buildErrorMessage(errors);
69  return std::move(writer.file_);
70  }
71 
72  private:
73  ToFileWriter() = default;
74 
75  // writers for every primitive
76  void writeNodes(const LaneletMap& map, const Projector& projector) {
77  auto& osmNodes = file_->nodes;
78  for (const auto& point : map.pointLayer) {
79  try {
80  const GPSPoint gpsPoint = projector.reverse(point);
81  osmNodes.emplace(point.id(), osm::Node(point.id(), getAttributes(point.attributes()), gpsPoint));
82  } catch (ReverseProjectionError& e) {
83  writeError(point.id(), e.what());
84  }
85  }
86  }
87 
88  void writeWays(const LaneletMap& map) {
89  auto& osmWays = file_->ways;
90  for (const auto& lineString : map.lineStringLayer) {
91  if (lineString.inverted()) {
92  writeOsmWay(lineString.invert(), osmWays);
93  } else {
94  writeOsmWay(lineString, osmWays);
95  }
96  }
97  for (const auto& polygon : map.polygonLayer) {
98  writeOsmWay(polygon, osmWays);
99  }
100  }
101 
102  void appendLanelets(const LaneletLayer& laneletLayer) {
103  for (const auto& lanelet : laneletLayer) {
104  const auto id = lanelet.id();
105  auto attributes = getAttributes(lanelet.attributes());
106  attributes.emplace(AttributeNamesString::Type, AttributeValueString::Lanelet);
107  auto& insertedRelation = file_->relations.emplace(id, osm::Relation(id, attributes)).first->second;
108  auto& members = insertedRelation.members;
109  tryInsertMembers(members, RoleNameString::Left, lanelet.leftBound().id(), file_->ways, id);
110  tryInsertMembers(members, RoleNameString::Right, lanelet.rightBound().id(), file_->ways, id);
111  if (lanelet.hasCustomCenterline()) {
112  tryInsertMembers(members, RoleNameString::Centerline, lanelet.centerline().id(), file_->ways, id);
113  }
114  for (const auto& regElem : lanelet.regulatoryElements()) {
115  tryInsertMembers(members, RoleNameString::RegulatoryElement, regElem->id(), file_->relations, id);
116  }
117  }
118  }
119 
120  void appendAreas(const AreaLayer& areaLayer) {
121  for (const auto& area : areaLayer) {
122  const auto id = area.id();
123  auto attributes = getAttributes(area.attributes());
124  attributes.emplace(AttributeNamesString::Type, AttributeValueString::Multipolygon);
125  auto& insertedRelation = file_->relations.emplace(id, osm::Relation(id, attributes)).first->second;
126  auto outerIds = area.outerBoundPolygon().ids();
127  auto innerIds = utils::concatenate(area.innerBoundPolygons(), [](const auto& elem) { return elem.ids(); });
128  auto& members = insertedRelation.members;
129  for (const auto& outerId : outerIds) {
130  tryInsertMembers(members, RoleNameString::Outer, outerId, file_->ways, id);
131  }
132  for (const auto& innerId : innerIds) {
133  tryInsertMembers(members, RoleNameString::Inner, innerId, file_->ways, id);
134  }
135  for (const auto& regElem : area.regulatoryElements()) {
136  tryInsertMembers(members, RoleNameString::RegulatoryElement, regElem->id(), file_->relations, id);
137  }
138  }
139  }
140 
141  void resolveUnparsedMembers(std::vector<UnresolvedRole>& unparsedMembers) {
142  auto& relations = file_->relations;
143  for (const auto& param : unparsedMembers) {
144  const auto id = param.relationId;
145  try {
146  assert(*param.location == nullptr);
147  *param.location = &relations.at(param.referencedRoleId);
148  } catch (std::out_of_range&) {
149  writeError(id, "Regulatory element has a lanelet/area "s + std::to_string(param.referencedRoleId) +
150  " that is not in the map!");
151  // ugly part: clean up the empty dummy at "location"
152  removeAndFixPlaceholders(param.location, relations.at(param.relationId).members, unparsedMembers);
153  }
154  }
155  }
156 
157  // helper functions for writing primitives
158  void writeError(Id id, const std::string& what) {
159  errors_.push_back("Error writing primitive "s + std::to_string(id) + ": " + what);
160  }
161 
162  static osm::Attributes getAttributes(const AttributeMap& attributes) {
163  osm::Attributes osmAttributes;
164  for (const auto& attr : attributes) {
165  osmAttributes.emplace(attr.first, attr.second.value());
166  }
167  return osmAttributes;
168  }
169 
170  template <typename PrimT>
171  void writeOsmWay(const PrimT& mapWay, osm::Ways& osmWays) {
172  const auto id = mapWay.id();
173  auto wayAttributes = getAttributes(mapWay.attributes());
174  if (std::is_same<PrimT, ConstPolygon3d>::value) {
175  wayAttributes.emplace(AttributeNamesString::Area, "true");
176  }
177  try {
178  const auto wayNodes =
179  utils::transform(mapWay, [&nodes = file_->nodes](const auto& elem) { return &nodes.at(elem.id()); });
180  osmWays.emplace(id, osm::Way(id, std::move(wayAttributes), std::move(wayNodes)));
181  } catch (NoSuchPrimitiveError& e) {
182  writeError(id, "Way has points that are not point layer: "s + e.what());
183  } catch (std::out_of_range&) {
184  writeError(id, "Way has a point that is not in the map!");
185  }
186  }
187 
188  using UnparsedLaneletParameter = std::tuple<std::string, ConstLanelet, osm::Relation*>;
189  using UnparsedAreaParameter = std::tuple<std::string, ConstArea, osm::Relation*>;
190  using UnparsedLaneletParameters = std::vector<UnparsedLaneletParameter>;
191  using UnparsedAreaParameters = std::vector<UnparsedAreaParameter>;
192 
193  class WriteRegulatoryElementVisitor : public RuleParameterVisitor {
194  public:
195  WriteRegulatoryElementVisitor(osm::File& file, ToFileWriter& writer) : file{file}, writer{writer} {}
196 
197  void operator()(const ConstPoint3d& p) override {
198  try {
199  currRelation->members.emplace_back(role, &file.nodes.at(p.id()));
200  } catch (std::out_of_range&) {
201  writer.writeError(
202  id, "Regulatory element has parameters that are not in the point layer: "s + std::to_string(p.id()));
203  }
204  }
205  void operator()(const ConstLineString3d& l) override {
206  try {
207  currRelation->members.emplace_back(role, &file.ways.at(l.id()));
208  } catch (std::out_of_range&) {
209  writer.writeError(
210  id, "Regulatory element has parameters that are not in the line string layer: "s + std::to_string(l.id()));
211  }
212  }
213  void operator()(const ConstPolygon3d& p) override {
214  try {
215  currRelation->members.emplace_back(role, &file.ways.at(p.id()));
216  } catch (std::out_of_range&) {
217  writer.writeError(
218  id, "Regulatory element has parameters that are not in the polygon layer: "s + std::to_string(p.id()));
219  }
220  }
221  void operator()(const ConstWeakLanelet& wll) override {
222  // try to lock
223  if (wll.expired()) {
224  writer.writeError(id, "Found an expired lanelet parameter with role " + role);
225  return;
226  }
227  // lanelets are not yet in the osm file
228  currRelation->members.emplace_back(role, nullptr);
230  UnresolvedRole{currRelation->id, wll.lock().id(), &currRelation->members.back().second});
231  }
232  void operator()(const ConstWeakArea& war) override {
233  // try to lock
234  if (war.expired()) {
235  writer.writeError(id, "Found an expired lanelet parameter with role " + role);
236  return;
237  }
238  // areas are not yet in the osm file
239  currRelation->members.emplace_back(role, nullptr);
241  UnresolvedRole{currRelation->id, war.lock().id(), &currRelation->members.back().second});
242  }
243  Id id{0};
244  osm::Relation* currRelation{nullptr};
245  osm::File& file;
246  ToFileWriter& writer;
247  std::vector<UnresolvedRole> unparsedLaneletAndAreaParameters;
248  };
249 
250  std::vector<UnresolvedRole> appendRegulatoryElements(const RegulatoryElementLayer& regElemLayer) {
251  WriteRegulatoryElementVisitor visitor(*file_, *this);
252  for (const auto& regElem : regElemLayer) {
253  const auto id = regElem->id();
254  auto attributes = getAttributes(regElem->attributes());
255  attributes.emplace(AttributeNamesString::Type, AttributeValueString::RegulatoryElement);
256  auto& insertedRelation = file_->relations.emplace(id, osm::Relation(id, attributes)).first->second;
257  visitor.id = id;
258  visitor.currRelation = &insertedRelation;
259  regElem->applyVisitor(visitor);
260  }
261  return visitor.unparsedLaneletAndAreaParameters;
262  }
263 
264  template <typename PrimitiveMap>
265  void tryInsertMembers(osm::Roles& insertMembers, const char* insertRole, Id insertId, PrimitiveMap& primitiveMap,
266  Id relationId) {
267  try {
268  insertMembers.emplace_back(insertRole, &primitiveMap.at(insertId));
269  } catch (std::out_of_range&) {
270  writeError(relationId, "Relation has a member with id "s + std::to_string(insertId) + " that is not in the map!");
271  }
272  }
273  void buildErrorMessage(ErrorMessages& errs) const {
274  errs.clear();
275  if (!errors_.empty()) {
276  errs.reserve(errors_.size() + 1);
277  errs.emplace_back("Errors ocurred while writing Lanelet Map:");
278  for (const auto& err : errors_) {
279  errs.emplace_back("\t- " + err);
280  }
281  }
282  }
284  std::unique_ptr<osm::File> file_{std::make_unique<osm::File>()};
285 };
286 
287 void testAndPrintLocaleWarning(ErrorMessages& errors) {
288  auto* decimalPoint = std::localeconv()->decimal_point;
289  if (decimalPoint == nullptr || *decimalPoint != '.') {
290  std::stringstream ss;
291  ss << "Warning: Current decimal point of the C locale is set to \""
292  << (decimalPoint == nullptr ? ' ' : *decimalPoint) << "\". This will lead to invalid osm output!\n";
293  errors.emplace_back(ss.str());
294  std::cerr << errors.back();
295  }
296 }
297 } // namespace
298 
299 void OsmWriter::write(const std::string& filename, const LaneletMap& laneletMap, ErrorMessages& errors, const io::Configuration& params) const {
300  testAndPrintLocaleWarning(errors);
301  auto file = toOsmFile(laneletMap, errors, params);
302  auto doc = osm::write(*file, params);
303  auto res = doc->save_file(filename.c_str(), " ");
304  if (!res) {
305  throw ParseError("Pugixml failed to write the map (unable to create file?)");
306  }
307 }
308 
309 std::unique_ptr<osm::File> OsmWriter::toOsmFile(const LaneletMap& laneletMap, ErrorMessages& errors, const io::Configuration& params) const {
310  return ToFileWriter::writeMap(laneletMap, projector(), errors, params);
311 }
312 } // namespace io_handlers
313 } // namespace lanelet
lanelet::ErrorMessages
std::vector< std::string > ErrorMessages
Definition: Io.h:11
lanelet
lanelet::io::Configuration
std::map< std::string, Attribute > Configuration
Definition: Configuration.h:8
file
osm::File & file
Definition: OsmHandlerWrite.cpp:245
p
BasicPoint p
Id
int64_t Id
location
osm::Primitive ** location
Definition: OsmHandlerWrite.cpp:22
id
Id id
Definition: OsmHandlerWrite.cpp:243
file_
std::unique_ptr< osm::File > file_
Definition: OsmHandlerWrite.cpp:284
OsmHandler.h
errors_
Errors errors_
Definition: OsmHandlerWrite.cpp:283
lanelet::osm::Ways
std::map< Id, Way > Ways
Definition: OsmFile.h:64
lanelet::LaneletMap
referencedRoleId
Id referencedRoleId
Definition: OsmHandlerWrite.cpp:21
PrimitiveLayer< RegulatoryElementPtr >
lanelet::osm::Errors
std::vector< std::string > Errors
Definition: OsmFile.h:20
lanelet::osm::Attributes
std::map< std::string, std::string > Attributes
Definition: OsmFile.h:17
lanelet::osm::Roles
std::deque< Role > Roles
Definition: OsmFile.h:19
currRelation
osm::Relation * currRelation
Definition: OsmHandlerWrite.cpp:244
lanelet::write
void write(const std::string &filename, const lanelet::LaneletMap &map, const Origin &origin=Origin::defaultOrigin(), ErrorMessages *errors=nullptr, const io::Configuration &params=io::Configuration())
writes a map to a file
Definition: Io.cpp:61
HybridMap< Attribute, decltype(AttributeNamesString::Map)&, AttributeNamesString::Map >
OsmFile.h
relationId
Id relationId
Definition: OsmHandlerWrite.cpp:20
unparsedLaneletAndAreaParameters
std::vector< UnresolvedRole > unparsedLaneletAndAreaParameters
Definition: OsmHandlerWrite.cpp:247
lanelet::ParseError
Error thrown if some error occured during the parsing of the file.
Definition: Exceptions.h:42
Factory.h
Exceptions.h
writer
ToFileWriter & writer
Definition: OsmHandlerWrite.cpp:246


lanelet2_io
Author(s): Fabian Poggenhans
autogenerated on Thu Mar 6 2025 03:26:03