#include "openlr/openlr_model_xml.hpp" #include "openlr/openlr_model.hpp" #include "geometry/mercator.hpp" #include "base/logging.hpp" #include #include #include #include "3party/pugixml/src/pugixml.hpp" using namespace std; 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; } std::optional DoubleFromXML(pugi::xml_node const & node) { return node ? node.text().as_double() : std::optional(); } 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(); } pugi::xml_node GetCoordinates(pugi::xml_node const & node) { return node.select_node(".//coordinates").node(); } bool IsLocationReferenceTag(pugi::xml_node const & node) { return node.select_node(".//olr:locationReference").node(); } bool IsCoordinatesTag(pugi::xml_node const & node) { 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.m_lat = ((lat - base::Sign(lat) * 0.5) * 360) / (1 << 24); latLon.m_lon = ((lon - base::Sign(lon) * 0.5) * 360) / (1 << 24); return true; } std::optional LatLonFormXML(pugi::xml_node const & node) { auto const lat = DoubleFromXML(node.child("latitude")); auto const lon = DoubleFromXML(node.child("longitude")); return lat && lon ? ms::LatLon(*lat, *lon) : ms::LatLon::Zero(); } 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.m_lat = prevCoord.m_lat + static_cast(lat) / kOpenlrDeltaFactor; latLon.m_lon = prevCoord.m_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 location 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; } bool CoordinatesFromXML(pugi::xml_node const & coordsNode, openlr::LinearLocationReference & locRef) { if (!coordsNode) { LOG(LERROR, ("Can't get .")); return false; } auto const latLonStart = LatLonFormXML(coordsNode.child("start")); auto const latLonEnd = LatLonFormXML(coordsNode.child("end")); if (!latLonStart || !latLonEnd) { LOG(LERROR, ("Can't get or of .")); return false; } LOG(LINFO, ("from:", *latLonStart, "to:", *latLonEnd)); locRef.m_points.resize(2); locRef.m_points[0].m_latLon = *latLonStart; locRef.m_points[1].m_latLon = *latLonEnd; return true; } } // namespace namespace openlr { bool ParseOpenlr(pugi::xml_document const & document, vector & segments) { for (auto const segmentXpathNode : document.select_nodes("//reportSegments")) { LinearSegment segment; auto const & node = segmentXpathNode.node(); if (!IsLocationReferenceTag(node) && !IsCoordinatesTag(node)) { LOG(LWARNING, ("A segment with a strange tag. It is not " " or , skipping...")); continue; } if (!SegmentFromXML(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; } if (IsLocationReferenceTag(segmentNode)) { auto const locRefNode = GetLinearLocationReference(segmentNode); auto const result = LinearLocationReferenceFromXML(locRefNode, segment.m_locationReference); if (result) segment.m_source = LinearSegmentSource::FromLocationReferenceTag; return result; } CHECK(IsCoordinatesTag(segmentNode), ()); auto const coordsNode = GetCoordinates(segmentNode); if (!CoordinatesFromXML(coordsNode, segment.m_locationReference)) return false; CHECK_EQUAL(segment.m_locationReference.m_points.size(), 2, ()); segment.m_locationReference.m_points[0].m_distanceToNextPoint = segment.m_segmentLengthMeters; segment.m_source = LinearSegmentSource::FromCoordinatesTag; return true; } } // namespace openlr