9 using namespace std::string_literals;
12 namespace io_handlers {
14 using Errors = std::vector<std::string>;
17 RegisterWriter<OsmWriter> regWriter;
19 struct UnresolvedRole {
25 void removeAndFixPlaceholders(osm::Primitive** toRemove,
osm::Roles& fromRoles,
26 std::vector<UnresolvedRole>& placeholders) {
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);
35 fromRoles.erase(remIt);
36 if (placeholderLocations.empty()) {
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);
45 for (
auto& placeholder : placeholders) {
46 auto it = newLocations.find(placeholder.location);
47 if (it != newLocations.end()) {
48 placeholder.location = it->second;
55 static std::unique_ptr<osm::File> writeMap(
const LaneletMap& laneletMap,
const Projector& projector,
59 writer.writeNodes(laneletMap, projector);
60 writer.writeWays(laneletMap);
64 writer.appendLanelets(laneletMap.laneletLayer);
65 writer.appendAreas(laneletMap.areaLayer);
68 writer.buildErrorMessage(errors);
69 return std::move(
writer.file_);
73 ToFileWriter() =
default;
76 void writeNodes(
const LaneletMap& map,
const Projector& projector) {
77 auto& osmNodes =
file_->nodes;
78 for (
const auto& point : map.pointLayer) {
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());
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);
94 writeOsmWay(lineString, osmWays);
97 for (
const auto& polygon : map.polygonLayer) {
98 writeOsmWay(polygon, osmWays);
102 void appendLanelets(
const LaneletLayer& laneletLayer) {
103 for (
const auto&
lanelet : laneletLayer) {
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);
114 for (
const auto& regElem :
lanelet.regulatoryElements()) {
115 tryInsertMembers(members, RoleNameString::RegulatoryElement, regElem->id(),
file_->relations,
id);
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);
132 for (
const auto& innerId : innerIds) {
133 tryInsertMembers(members, RoleNameString::Inner, innerId,
file_->ways,
id);
135 for (
const auto& regElem : area.regulatoryElements()) {
136 tryInsertMembers(members, RoleNameString::RegulatoryElement, regElem->id(),
file_->relations,
id);
141 void resolveUnparsedMembers(std::vector<UnresolvedRole>& unparsedMembers) {
142 auto& relations =
file_->relations;
143 for (
const auto& param : unparsedMembers) {
144 const auto id = param.relationId;
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!");
152 removeAndFixPlaceholders(param.location, relations.at(param.relationId).members, unparsedMembers);
158 void writeError(
Id id,
const std::string& what) {
159 errors_.push_back(
"Error writing primitive "s + std::to_string(
id) +
": " + what);
164 for (
const auto& attr : attributes) {
165 osmAttributes.emplace(attr.first, attr.second.value());
167 return osmAttributes;
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");
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!");
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>;
193 class WriteRegulatoryElementVisitor :
public RuleParameterVisitor {
197 void operator()(
const ConstPoint3d& p)
override {
200 }
catch (std::out_of_range&) {
202 id,
"Regulatory element has parameters that are not in the point layer: "s + std::to_string(
p.id()));
205 void operator()(
const ConstLineString3d& l)
override {
208 }
catch (std::out_of_range&) {
210 id,
"Regulatory element has parameters that are not in the line string layer: "s + std::to_string(l.id()));
213 void operator()(
const ConstPolygon3d& p)
override {
216 }
catch (std::out_of_range&) {
218 id,
"Regulatory element has parameters that are not in the polygon layer: "s + std::to_string(
p.id()));
221 void operator()(
const ConstWeakLanelet& wll)
override {
224 writer.writeError(
id,
"Found an expired lanelet parameter with role " + role);
232 void operator()(
const ConstWeakArea& war)
override {
235 writer.writeError(
id,
"Found an expired lanelet parameter with role " + role);
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;
258 visitor.currRelation = &insertedRelation;
259 regElem->applyVisitor(visitor);
261 return visitor.unparsedLaneletAndAreaParameters;
264 template <
typename PrimitiveMap>
265 void tryInsertMembers(
osm::Roles& insertMembers,
const char* insertRole,
Id insertId, PrimitiveMap& primitiveMap,
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!");
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);
284 std::unique_ptr<osm::File>
file_{std::make_unique<osm::File>()};
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();
300 testAndPrintLocaleWarning(errors);
301 auto file = toOsmFile(laneletMap, errors, params);
303 auto res = doc->save_file(filename.c_str(),
" ");
305 throw ParseError(
"Pugixml failed to write the map (unable to create file?)");
310 return ToFileWriter::writeMap(laneletMap, projector(), errors, params);