#include "openlr/openlr_model_xml.hpp" #include "openlr/openlr_model.hpp" #include "geometry/mercator.hpp" #include "base/logging.hpp" #include "std/cstring.hpp" #include "std/type_traits.hpp" #include "3party/pugixml/src/pugixml.hpp" namespace // Primitive utilities to handle simple OpenLR-like XML data. { template bool IntegerFromXML(pugi::xml_node const & node, Value & value) { if (!node) return false; value = static_cast(is_signed::value ? node.text().as_int() : node.text().as_uint()); return true; } bool GetLatLon(pugi::xml_node const & node, int32_t & lat, int32_t & lon) { if (!IntegerFromXML(node.child("olr:latitude"), lat) || !IntegerFromXML(node.child("olr:longitude"), lon)) { return false; } return true; } // This helper is used to parse records like this: // template bool TableValueFromXML(pugi::xml_node const & node, Value & value) { if (!node) return false; value = static_cast(is_signed::value ? node.attribute("olr:code").as_int() : node.attribute("olr:code").as_uint()); return true; } pugi::xml_node GetLinearLocationReference(pugi::xml_node const & node) { // There should be only one linear location reference child of a location reference. return node.select_node(".//olr:locationReference/olr:optionLinearLocationReference").node(); } bool NoLocationReferenceButCoordinates(pugi::xml_node const & node) { if (node.select_node(".//olr:locationReference").node()) return false; return node.select_node("coordinates").node(); } // This helper is used do deal with xml nodes of the form // // integer // template bool ValueFromXML(pugi::xml_node const & node, Value & value) { auto const valueNode = node.child("olr:value"); return IntegerFromXML(valueNode, value); } template bool ParseValueIfExists(pugi::xml_node const & node, Value & value) { if (!node) return true; return ValueFromXML(node, value); } } // namespace namespace // OpenLR tools and abstractions { bool FirstCoordinateFromXML(pugi::xml_node const & node, ms::LatLon & latLon) { int32_t lat, lon; if (!GetLatLon(node.child("olr:coordinate"), lat, lon)) return false; latLon.lat = ((lat - my::Sign(lat) * 0.5) * 360) / (1 << 24); latLon.lon = ((lon - my::Sign(lon) * 0.5) * 360) / (1 << 24); return true; } bool CoordinateFromXML(pugi::xml_node const & node, ms::LatLon const & prevCoord, ms::LatLon & latLon) { int32_t lat, lon; if (!GetLatLon(node.child("olr:coordinate"), lat, lon)) return false; // This constant is provided by the given OpenLR variant // with no special meaning and is used as a factor to store doubles as ints. auto const kOpenlrDeltaFactor = 100000; latLon.lat = prevCoord.lat + static_cast(lat) / kOpenlrDeltaFactor; latLon.lon = prevCoord.lon + static_cast(lon) / kOpenlrDeltaFactor; return true; } bool LinePropertiesFromXML(pugi::xml_node const & linePropNode, openlr::LocationReferencePoint & locPoint) { if (!linePropNode) { LOG(LERROR, ("linePropNode is NULL")); return false; } if (!TableValueFromXML(linePropNode.child("olr:frc"), locPoint.m_functionalRoadClass)) { LOG(LERROR, ("Can't parse functional road class")); return false; } if (!TableValueFromXML(linePropNode.child("olr:fow"), locPoint.m_formOfWay)) { LOG(LERROR, ("Can't parse form of a way")); return false; } if (!ValueFromXML(linePropNode.child("olr:bearing"), locPoint.m_bearing)) { LOG(LERROR, ("Can't parse bearing")); return false; } return true; } bool PathPropertiesFromXML(pugi::xml_node const & locPointNode, openlr::LocationReferencePoint & locPoint) { // Last point does not contain path properties. if (strcmp(locPointNode.name(), "olr:last") == 0) return true; auto const propNode = locPointNode.child("olr:pathProperties"); if (!propNode) { LOG(LERROR, ("Can't parse path properties")); return false; } if (!ValueFromXML(propNode.child("olr:dnp"), locPoint.m_distanceToNextPoint)) { LOG(LERROR, ("Can't parse dnp")); return false; } if (!TableValueFromXML(propNode.child("olr:lfrcnp"), locPoint.m_lfrcnp)) { LOG(LERROR, ("Can't parse lfrcnp")); return false; } auto const directionNode = propNode.child("olr:againstDrivingDirection"); if (!directionNode) { LOG(LERROR, ("Can't parse driving direction")); return false; } locPoint.m_againstDrivingDirection = directionNode.text().as_bool(); return true; } bool LocationReferencePointFromXML(pugi::xml_node const & locPointNode, openlr::LocationReferencePoint & locPoint) { if (!FirstCoordinateFromXML(locPointNode, locPoint.m_latLon)) { LOG(LERROR, ("Can't get first coordinate")); return false; } return LinePropertiesFromXML(locPointNode.child("olr:lineProperties"), locPoint) && PathPropertiesFromXML(locPointNode, locPoint); } bool LocationReferencePointFromXML(pugi::xml_node const & locPointNode, ms::LatLon const & firstPoint, openlr::LocationReferencePoint & locPoint) { if (!CoordinateFromXML(locPointNode, firstPoint, locPoint.m_latLon)) { LOG(LERROR, ("Can't get last coordinate")); return false; } return LinePropertiesFromXML(locPointNode.child("olr:lineProperties"), locPoint) && PathPropertiesFromXML(locPointNode, locPoint); } bool LinearLocationReferenceFromXML(pugi::xml_node const & locRefNode, openlr::LinearLocationReference & locRef) { if (!locRefNode) { LOG(LERROR, ("Can't get loaction reference")); return false; } { openlr::LocationReferencePoint point; if (!LocationReferencePointFromXML(locRefNode.child("olr:first"), point)) return false; locRef.m_points.push_back(point); } for (auto const pointNode : locRefNode.select_nodes("olr:intermediates")) { openlr::LocationReferencePoint point; if (!LocationReferencePointFromXML(pointNode.node(), locRef.m_points.back().m_latLon, point)) return false; locRef.m_points.push_back(point); } { openlr::LocationReferencePoint point; if (!LocationReferencePointFromXML(locRefNode.child("olr:last"), locRef.m_points.back().m_latLon, point)) return false; locRef.m_points.push_back(point); } if (!ParseValueIfExists(locRefNode.child("olr:positiveOffset"), locRef.m_positiveOffsetMeters)) { LOG(LERROR, ("Can't parse positive offset")); return false; } if(!ParseValueIfExists(locRefNode.child("olr:negativeOffset"), locRef.m_negativeOffsetMeters)) { LOG(LERROR, ("Can't parse negative offset")); return false; } return true; } } // namespace namespace openlr { bool ParseOpenlr(pugi::xml_document const & document, vector & segments) { for (auto const segmentXpathNode : document.select_nodes("//reportSegments")) { LinearSegment segment; if (NoLocationReferenceButCoordinates(segmentXpathNode.node())) { LOG(LWARNING, ("A segment with instead of " "encounted, skipping...")); continue; } if (!SegmentFromXML(segmentXpathNode.node(), segment)) return false; segments.push_back(segment); } return true; } bool SegmentFromXML(pugi::xml_node const & segmentNode, LinearSegment & segment) { CHECK(segmentNode, ()); if (!IntegerFromXML(segmentNode.child("ReportSegmentID"), segment.m_segmentId)) { LOG(LERROR, ("Can't parse segment id")); return false; } if (!IntegerFromXML(segmentNode.child("segmentLength"), segment.m_segmentLengthMeters)) { LOG(LERROR, ("Can't parse segment length")); return false; } auto const locRefNode = GetLinearLocationReference(segmentNode); return LinearLocationReferenceFromXML(locRefNode, segment.m_locationReference); } } // namespace openlr