1 #include <lanelet2_core/geometry/LineString.h>
2 #include <lanelet2_core/geometry/Polygon.h>
4 #include <boost/geometry/algorithms/is_valid.hpp>
15 using namespace std::string_literals;
18 namespace io_handlers {
20 using Errors = std::vector<std::string>;
27 BasicPolygon2d ls(utils::concatenate(lss, [](
const auto& elem) {
return to2D(elem).basicLineString(); }));
28 return boost::geometry::is_valid(ls);
32 for (
auto& ls : lss) {
35 std::reverse(lss.begin(), lss.end());
38 template <
typename PrimT>
39 PrimT getDummy(
Id id) {
44 return std::make_shared<GenericRegulatoryElement>(std::make_shared<RegulatoryElementData>(
id));
47 Errors buildErrorMessage(
const std::string& errorIntro,
const Errors& errors) {
51 Errors message{errorIntro};
52 message.reserve(errors.size() + 1);
53 for (
const auto& error : errors) {
54 message.push_back(
"\t- " + error);
59 class FromFileLoader {
62 FromFileLoader loader;
64 loader.loadNodes(
file.nodes, projector);
65 loader.loadWays(
file.ways);
66 auto laneletsWithRelation = loader.loadLanelets(
file.relations);
67 auto areasWithRelation = loader.loadAreas(
file.relations);
69 loader.loadRegulatoryElements(
file.relations);
70 loader.addRegulatoryElements(laneletsWithRelation);
71 loader.addRegulatoryElements(areasWithRelation);
72 errors = std::move(loader.errors_);
73 return std::make_unique<LaneletMap>(loader.lanelets_, loader.areas_, loader.regulatoryElements_, loader.polygons_,
74 loader.lineStrings_, loader.points_);
78 template <
typename PrimT>
79 using PrimitiveWithRegulatoryElement = std::pair<PrimT, const osm::Relation*>;
81 template <
typename PrimT>
82 using PrimitivesWithRegulatoryElement = std::vector<PrimitiveWithRegulatoryElement<PrimT>>;
84 using AreasWithRegulatoryElements = PrimitivesWithRegulatoryElement<Area>;
85 using LaneletsWithRegulatoryElements = PrimitivesWithRegulatoryElement<Lanelet>;
87 FromFileLoader() =
default;
90 for (
const auto& nodeElem : nodes) {
91 const auto& node = nodeElem.second;
93 points_.emplace(node.id,
Point3d(node.id, projector.
forward(node.point), getAttributes(node.attributes)));
95 parserError(node.id, e.what());
100 for (
const auto& wayElem : ways) {
101 const auto& way = wayElem.second;
104 points = utils::transform(way.nodes,
105 [
this, &way](
const auto& n) { return this->getOrGetDummy(points_, n->id, way.id); });
106 if (points.empty()) {
107 parserError(way.id,
"Ways must have at least one point!");
111 const auto id = way.id;
112 const auto attributes = getAttributes(way.attributes);
115 auto isArea = attributes.find(AttributeNamesString::Area);
116 if (isArea != attributes.end() && isArea->second.asBool().get_value_or(
false)) {
128 LaneletsWithRegulatoryElements llWithRegulatoryElement;
129 for (
const auto& relElem : relations) {
130 const auto& llElem = relElem.second;
131 if (!isType<AttributeValueString::Lanelet>(llElem)) {
134 const auto id = llElem.id;
135 const auto attributes = getAttributes(llElem.attributes);
136 auto left = getLaneletBorder(llElem, RoleNameString::Left);
137 auto right = getLaneletBorder(llElem, RoleNameString::Right);
140 std::tie(left, right) = geometry::align(left, right);
144 if (
findRole(llElem.members, RoleNameString::Centerline) != llElem.members.end()) {
145 auto center = getLaneletBorder(llElem, RoleNameString::Centerline);
152 if (
findRole(llElem.members, RoleNameString::RegulatoryElement) != llElem.members.end()) {
153 llWithRegulatoryElement.push_back(std::make_pair(
lanelet, &llElem));
156 return llWithRegulatoryElement;
161 AreasWithRegulatoryElements arWithRegulatoryElement;
162 for (
const auto& relElem : relations) {
163 const auto& arElem = relElem.second;
164 if (!isType<AttributeValueString::Multipolygon>(arElem)) {
167 const auto id = arElem.id;
168 const auto attributes = getAttributes(arElem.attributes);
170 auto outerRing = getOuterRing(arElem);
171 if (outerRing.empty()) {
176 Area area(
id, outerRing, getInnerRing(arElem), attributes);
180 if (
findRole(arElem.members, RoleNameString::RegulatoryElement) != arElem.members.end()) {
181 arWithRegulatoryElement.push_back(std::make_pair(area, &arElem));
184 return arWithRegulatoryElement;
188 for (
const auto& relElem : relations) {
189 const auto& regElem = relElem.second;
190 if (!isType<AttributeValueString::RegulatoryElement>(regElem)) {
193 const auto id = regElem.id;
194 const auto attributes = getAttributes(regElem.attributes);
195 const auto type = attributes.find(AttributeName::Subtype);
196 if (type == attributes.end()) {
197 parserError(
id,
"Regulatory element has no 'subtype' tag.");
200 auto rules = getRulesForRegulatoryElement(
id, regElem.members);
201 auto regelemData = std::make_shared<RegulatoryElementData>(
id, rules, attributes);
202 auto regelemType = type->second.value();
204 auto regElem = RegulatoryElementFactory::create(regelemType, regelemData);
206 }
catch (std::exception& e) {
207 parserError(
id,
"Creating a regulatory element of type "s + regelemType +
" failed: " + e.what());
212 template <
typename PrimT>
213 void addRegulatoryElements(std::vector<std::pair<PrimT, const osm::Relation*>>& addTos) {
214 for (
auto& addTo : addTos) {
216 auto regElem = getOrGetDummy(regulatoryElements_, role.second->id, addTo.first.id());
217 addTo.first.addRegulatoryElement(regElem);
223 template <const
char* Type>
225 auto attr =
relation.attributes.find(AttributeNamesString::Type);
226 return attr !=
relation.attributes.end() && attr->second ==
Type;
231 for (
const auto& osmAttr : osmAttributes) {
238 size_t numMembers = 0;
240 if (numMembers != 1) {
241 parserError(llElem.
id,
"Lanelet has not exactly one "s + role +
" border!");
245 if (member->second->type() != AttributeValueString::Way) {
246 parserError(llElem.
id,
"Lanelet "s + role +
" border is not of type way!");
249 return getOrGetDummy(
lineStrings_, member->second->id, llElem.
id);
255 if (member.second->type() != AttributeValueString::Way) {
256 auto msg = roleName +
" ring must consist of ways but id " + std::to_string(member.second->id) +
257 " is of type " + member.second->type() +
"!";
258 msg[0] = std::toupper(msg[0]);
259 this->parserError(refId, msg);
264 this->parserError(refId,
"Failed to get id "s + std::to_string(member.second->id) +
" from map");
267 linestrings.push_back(elem->second);
273 auto outerLs = getLinestrings(area.
members, RoleNameString::Outer, area.
id);
274 if (outerLs.empty()) {
275 parserError(area.
id,
"Areas must have at least one outer border!");
278 auto outerRings = assembleBoundary(outerLs, area.
id);
279 if (outerRings.size() != 1) {
280 parserError(area.
id,
"Areas must have exactly one outer ring!");
283 return outerRings.front();
286 std::vector<LineStrings3d> getInnerRing(
const osm::Relation& area) {
287 auto innerLs = getLinestrings(area.
members, RoleNameString::Inner, area.
id);
288 return assembleBoundary(innerLs, area.
id);
293 for (
const auto& memberPair : roles) {
294 const auto& member = memberPair.second;
295 if (member->type() == AttributeValueString::Node) {
296 auto newMember = getOrGetDummy(
points_, member->id, currElemId);
297 rules[memberPair.first].emplace_back(newMember);
298 }
else if (member->type() == AttributeValueString::Way) {
301 auto newMember = getOrGetDummy(
polygons_, member->id, currElemId);
302 rules[memberPair.first].emplace_back(newMember);
304 auto newMember = getOrGetDummy(
lineStrings_, member->id, currElemId);
305 rules[memberPair.first].emplace_back(newMember);
307 }
else if (member->type() == AttributeValueString::Relation) {
309 auto type = member->attributes.
find(AttributeNamesString::Type);
310 if (type == member->attributes.end()) {
311 parserError(currElemId,
312 "Relation refers to another relation "s + std::to_string(member->id) +
" without a type tag!");
313 }
else if (type->second == AttributeValueString::Lanelet) {
314 auto newMember = getOrGetDummy(
lanelets_, member->id, currElemId);
315 rules[memberPair.first].emplace_back(newMember);
316 }
else if (type->second == AttributeValueString::Multipolygon) {
317 auto newMember = getOrGetDummy(
areas_, member->id, currElemId);
318 rules[memberPair.first].emplace_back(newMember);
319 }
else if (type->second == AttributeValueString::RegulatoryElement) {
320 parserError(currElemId,
321 "Regulatory element refers to another "
322 "regulatory element. This is not "
325 parserError(currElemId,
"Member of regulatory_element has unsupported type "s + type->second);
332 std::vector<LineStrings3d> assembleBoundary(
LineStrings3d lineStrings,
Id id) {
333 std::reverse(lineStrings.begin(), lineStrings.end());
334 std::vector<LineStrings3d> rings;
336 while (!lineStrings.empty()) {
337 auto& currRing = rings.back();
338 if (currRing.empty()) {
339 currRing.push_back(lineStrings.back());
340 lineStrings.pop_back();
342 const auto lastId = currRing.back().back().id();
343 auto elem = std::find_if(lineStrings.rbegin(), lineStrings.rend(), [lastId](
const auto& elem) {
344 return elem.back().id() == lastId || elem.front().id() == lastId;
347 if (elem == lineStrings.rend()) {
348 parserError(
id,
"Could not complete boundary around linestring " + std::to_string(currRing.back().id()));
353 auto newLineString = *elem;
354 lineStrings.erase(std::next(elem).base());
355 if (newLineString.back().id() == lastId) {
356 newLineString = newLineString.invert();
358 currRing.push_back(newLineString);
362 if (currRing.back().back().id() == currRing.front().front().id()) {
364 if (!isValid(currRing)) {
366 if (!isValid(currRing)) {
368 parserError(
id,
"Failed to generate boundary (self-intersecting?)");
379 template <
typename PrimT>
380 PrimT getOrGetDummy(
const typename std::unordered_map<Id, PrimT>& map,
Id id,
Id currentPrimitiveId) {
383 }
catch (std::out_of_range&) {
384 parserError(currentPrimitiveId,
"Failed to get id "s + std::to_string(
id) +
" from map");
385 return getDummy<PrimT>(
id);
389 void parserError(
Id id,
const std::string& what) {
390 auto errstr =
"Error parsing primitive "s + std::to_string(
id) +
": " + what;
403 template <
typename MapT>
404 void registerIds(
const MapT& map) {
406 utils::registerId(map.rbegin()->first);
411 auto* decimalPoint = std::localeconv()->decimal_point;
412 if (decimalPoint ==
nullptr || *decimalPoint !=
'.') {
413 std::stringstream ss;
414 ss <<
"Warning: Current decimal point of the C locale is set to \""
415 << (decimalPoint ==
nullptr ?
' ' : *decimalPoint) <<
"\". The loaded map will have wrong coordinates!\n";
416 errors.emplace_back(ss.str());
417 std::cerr << errors.back();
422 std::unique_ptr<LaneletMap> OsmParser::parse(
const std::string& filename,
ErrorMessages& errors)
const {
424 pugi::xml_document doc;
425 auto result = doc.load_file(filename.c_str());
427 throw lanelet::ParseError(
"Errors occured while parsing osm file: "s + result.description());
430 testAndPrintLocaleWarning(osmReadErrors);
432 auto map = fromOsmFile(
file, errors);
434 registerIds(
file.nodes);
435 registerIds(
file.ways);
436 registerIds(
file.relations);
437 errors = buildErrorMessage(
"Errors ocurred while parsing Lanelet Map:", utils::concatenate({osmReadErrors, errors}));
442 return FromFileLoader::loadMap(
file, projector(), errors);