diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/mainwindow.cpp b/openlr/openlr_match_quality/openlr_assessment_tool/mainwindow.cpp index 227c8338f3..4578b7ed1b 100644 --- a/openlr/openlr_match_quality/openlr_assessment_tool/mainwindow.cpp +++ b/openlr/openlr_match_quality/openlr_assessment_tool/mainwindow.cpp @@ -67,7 +67,7 @@ public: m_drapeApi.Clear(); } - void VisualizeGoldenPath(std::vector const & points) + void VisualizeGoldenPath(std::vector const & points) override { ClearAllPaths(); @@ -152,12 +152,12 @@ public: size_t pointIndex = 0; ft.ForEachPoint([&points, &p, &ft, &pointIndex](m2::PointD const & fp) { - ++pointIndex; if (fp.EqualDxDy(p, 1e-4)) { points.emplace_back(ft.GetID(), pointIndex); p = fp; } + ++pointIndex; }, FeatureType::BEST_GEOMETRY); }, @@ -253,8 +253,8 @@ void MainWindow::CreateTrafficPanel(string const & dataFilePath) make_unique(m_framework), make_unique(m_framework)); - connect(m_mapWidget, SIGNAL(TrafficMarkupClick(m2::PointD const &)), - m_trafficMode, SLOT(OnClick(m2::PointD const &))); + connect(m_mapWidget, &MapWidget::TrafficMarkupClick, + m_trafficMode, &TrafficMode::OnClick); m_docWidget = new QDockWidget(tr("Routes"), this); addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_docWidget); diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.cpp b/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.cpp index 5794c12cfb..43ae4434d3 100644 --- a/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.cpp +++ b/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.cpp @@ -2,6 +2,8 @@ #include "map/framework.hpp" +#include + MapWidget::MapWidget(Framework & framework, bool apiOpenGLES3, QWidget * parent) : Base(framework, apiOpenGLES3, parent) { @@ -16,6 +18,6 @@ void MapWidget::mousePressEvent(QMouseEvent * e) if (m_mode == Mode::TrafficMarkup) { auto pt = GetDevicePoint(e); - emit TrafficMarkupClick(m_framework.PtoG(pt)); + emit TrafficMarkupClick(m_framework.PtoG(pt), e->button()); } } diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.hpp b/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.hpp index 476aa984c9..aa315e18a7 100644 --- a/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.hpp +++ b/openlr/openlr_match_quality/openlr_assessment_tool/map_widget.hpp @@ -29,7 +29,7 @@ public: void SetTrafficMarkupMode() { m_mode = Mode::TrafficMarkup; } signals: - void TrafficMarkupClick(m2::PointD const & p); + void TrafficMarkupClick(m2::PointD const & p, Qt::MouseButton const b); protected: void mousePressEvent(QMouseEvent * e) override; diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.cpp b/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.cpp index e99fb36db5..57701daf46 100644 --- a/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.cpp +++ b/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.cpp @@ -5,13 +5,10 @@ #include "indexer/index.hpp" #include "indexer/scales.hpp" -#include "base/macros.hpp" -#include "base/stl_add.hpp" - -#include "3party/pugixml/src/pugixml.hpp" - #include +#include + namespace { std::vector GetPoints(openlr::LinearSegment const & segment) @@ -128,9 +125,14 @@ TrafficMode::TrafficMode(std::string const & dataFileName, auto const xmlSegment = xpathNode.node(); auto & segment = m_segments.back(); - // TODO(mgsergio): Unify error handling interface of openlr_xml_mode and decoded_path parsers. - if (!openlr::SegmentFromXML(xmlSegment.child("reportSegments"), segment.GetPartnerSegment())) - MYTHROW(TrafficModeError, ("An error occured while parsing: can't parse segment")); + { + // TODO(mgsergio): Unify error handling interface of openlr_xml_mode and decoded_path parsers. + auto const partnerSegment = xmlSegment.child("reportSegments"); + if (!openlr::SegmentFromXML(partnerSegment, segment.GetPartnerSegment())) + MYTHROW(TrafficModeError, ("An error occured while parsing: can't parse segment")); + + segment.SetPartnerXML(partnerSegment); + } if (auto const route = xmlSegment.child("Route")) { @@ -157,8 +159,32 @@ TrafficMode::TrafficMode(std::string const & dataFileName, bool TrafficMode::SaveSampleAs(std::string const & fileName) const { - // TODO(mgsergio): Remove #include "base/macros.hpp" when implemented; - NOTIMPLEMENTED(); + CHECK(!fileName.empty(), ("Can't save to an empty file.")); + + pugi::xml_document result; + + for (auto const & sc : m_segments) + { + auto segment = result.append_child("Segment"); + segment.append_copy(sc.GetPartnerXML()); + if (auto const & path = sc.GetMatchedPath()) + { + auto node = segment.append_child("Route"); + openlr::PathToXML(*path, node); + } + if (auto const & path = sc.GetFakePath()) + { + auto node = segment.append_child("FakeRoute"); + openlr::PathToXML(*path, node); + } + if (auto const & path = sc.GetGoldenPath()) + { + auto node = segment.append_child("GoldenRoute"); + openlr::PathToXML(*path, node); + } + } + + result.save_file(fileName.data(), " " /* indent */); return true; } @@ -196,18 +222,18 @@ void TrafficMode::OnItemSelected(QItemSelection const & selected, QItemSelection // TODO(mgsergio): Use algo for center calculation. // Now viewport is set to the first point of the first segment. CHECK_LESS(row, m_segments.size(), ()); - auto & segment = m_segments[row]; - auto const partnerSegmentId = segment.GetPartnerSegmentId(); + m_currentSegment = &m_segments[row]; + auto const partnerSegmentId = m_currentSegment->GetPartnerSegmentId(); // TODO(mgsergio): Maybe we shold show empty paths. - CHECK(segment.GetMatchedPath(), ("Empty mwm segments for partner id", partnerSegmentId)); + CHECK(m_currentSegment->GetMatchedPath(), ("Empty mwm segments for partner id", partnerSegmentId)); - auto const & path = *segment.GetMatchedPath(); + auto const & path = *m_currentSegment->GetMatchedPath(); auto const & firstEdge = path.front(); m_drawerDelegate->ClearAllPaths(); m_drawerDelegate->SetViewportCenter(GetStart(firstEdge)); - m_drawerDelegate->DrawEncodedSegment(GetPoints(segment.GetPartnerSegment())); + m_drawerDelegate->DrawEncodedSegment(GetPoints(m_currentSegment->GetPartnerSegment())); m_drawerDelegate->DrawDecodedSegments(GetPoints(path)); } @@ -219,7 +245,6 @@ Qt::ItemFlags TrafficMode::flags(QModelIndex const & index) const return QAbstractItemModel::flags(index); } - void TrafficMode::StartBuildingPath() { if (m_buildingPath) @@ -232,21 +257,63 @@ void TrafficMode::StartBuildingPath() void TrafficMode::PushPoint(m2::PointD const & coord, std::vector const & points) { impl::RoadPointCandidate point(points, coord); - if (!m_path.empty()) - m_path.back().ActivatePoint(point); - m_path.push_back(point); + if (!m_goldenPath.empty()) + m_goldenPath.back().ActivatePoint(point); + m_goldenPath.push_back(point); } void TrafficMode::PopPoint() { - CHECK(!m_path.empty(), ("Attempt to pop point from an empty path.")); - m_path.pop_back(); + CHECK(!m_goldenPath.empty(), ("Attempt to pop point from an empty path.")); + m_goldenPath.pop_back(); } void TrafficMode::CommitPath() { + // TODO(mgsergio): Make this situation impossible. Make the first segment selected by default + // for example. + CHECK(m_currentSegment, ("No segments selected")); + if (!m_buildingPath) MYTHROW(TrafficModeError, ("Path building is not started")); + + if (m_goldenPath.empty()) + { + LOG(LDEBUG, ("Golden path is empty :'(")); + return; + } + + CHECK_NOT_EQUAL(m_goldenPath.size(), 1, ("Path cannot consist of only one point")); + + // Activate last point. Since no more points will be availabe we link it to the same + // feature as the previous one was linked to. + m_goldenPath.back().ActivatePoint(m_goldenPath[GetPointsCount() - 2]); + + openlr::Path path; + for (size_t i = 1; i < GetPointsCount(); ++i) + { + FeatureID fid, prevFid; + size_t segId, prevSegId; + + auto const prevPoint = m_goldenPath[i - 1]; + auto point = m_goldenPath[i]; + + // The start and the end of the edge should lie on the same feature. + point.ActivatePoint(prevPoint); + + std::tie(prevFid, prevSegId) = prevPoint.GetPoint(); + std::tie(fid, segId) = point.GetPoint(); + + path.emplace_back( + fid, + prevSegId < segId /* forward */, + prevSegId, + routing::Junction(prevPoint.GetCoordinate(), 0 /* altitude */), + routing::Junction(point.GetCoordinate(), 0 /* altitude */) + ); + } + + m_currentSegment->GetGoldenPath() = path; m_buildingPath = false; } @@ -256,30 +323,30 @@ void TrafficMode::RollBackPath() size_t TrafficMode::GetPointsCount() const { - return m_path.size(); + return m_goldenPath.size(); } m2::PointD const & TrafficMode::GetPoint(size_t const index) const { - return m_path[index].GetCoordinate(); + return m_goldenPath[index].GetCoordinate(); } m2::PointD const & TrafficMode::GetLastPoint() const { - CHECK(!m_path.empty(), ("Attempt to get point from an empty path.")); - return m_path.back().GetCoordinate(); + CHECK(!m_goldenPath.empty(), ("Attempt to get point from an empty path.")); + return m_goldenPath.back().GetCoordinate(); } std::vector TrafficMode::GetCoordinates() const { std::vector coordinates; - for (auto const & roadPoint : m_path) + for (auto const & roadPoint : m_goldenPath) coordinates.push_back(roadPoint.GetCoordinate()); return coordinates; } // TODO(mgsergio): Draw the first point when the path size is 1. -void TrafficMode::HandlePoint(m2::PointD clickPoint) +void TrafficMode::HandlePoint(m2::PointD clickPoint, Qt::MouseButton const button) { auto const currentPathLength = GetPointsCount(); auto const lastClickedPoint = currentPathLength != 0 @@ -324,34 +391,35 @@ void TrafficMode::HandlePoint(m2::PointD clickPoint) m_drawerDelegate->VisualizePoints(reachablePoints); m_drawerDelegate->VisualizePoints({clickPoint}); break; - case ClickType::Remove: - // if (button == Qt::MouseButton::LeftButton) // RemovePoint - // { - m_drawerDelegate->CleanAllVisualizedPoints(); - // TODO(mgsergio): Remove only golden path. - m_drawerDelegate->ClearAllPaths(); - - PopPoint(); - if (m_path.empty()) + case ClickType::Remove: // TODO(mgsergio): Rename this case. + if (button == Qt::MouseButton::LeftButton) // RemovePoint { - m_drawerDelegate->VisualizePoints( - m_pointsDelegate->GetAllJunctionPointsInViewPort()); - } - else - { - auto const & prevPoint = GetLastPoint(); - m_drawerDelegate->VisualizePoints( - GetReachablePoints(GetLastPoint(), GetCoordinates(), *m_pointsDelegate, - 1 /* lookBackIndex */)); - } + m_drawerDelegate->CleanAllVisualizedPoints(); + // TODO(mgsergio): Remove only golden path. + m_drawerDelegate->ClearAllPaths(); - if (GetPointsCount() > 1) - m_drawerDelegate->VisualizeGoldenPath(GetCoordinates()); - // } - // else if (botton == Qt::MouseButton::RightButton) - // { - // // TODO(mgsergio): Implement comit path. - // } + PopPoint(); + if (m_goldenPath.empty()) + { + m_drawerDelegate->VisualizePoints( + m_pointsDelegate->GetAllJunctionPointsInViewPort()); + } + else + { + // TODO(mgsergioe): Warning unused! Check this staff. + auto const & prevPoint = GetLastPoint(); + m_drawerDelegate->VisualizePoints( + GetReachablePoints(GetLastPoint(), GetCoordinates(), *m_pointsDelegate, + 1 /* lookBackIndex */)); + } + + if (GetPointsCount() > 1) + m_drawerDelegate->VisualizeGoldenPath(GetCoordinates()); + } + else if (button == Qt::MouseButton::RightButton) + { + CommitPath(); + } break; case ClickType::Miss: LOG(LDEBUG, ("You miss")); diff --git a/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.hpp b/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.hpp index 97a46c63af..ff63e140e0 100644 --- a/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.hpp +++ b/openlr/openlr_match_quality/openlr_assessment_tool/traffic_mode.hpp @@ -7,6 +7,8 @@ #include "base/exception.hpp" +#include "3party/pugixml/src/pugixml.hpp" + #include #include #include @@ -15,6 +17,7 @@ #include #include +#include class Index; class QItemSelection; @@ -47,6 +50,7 @@ private: m2::PointD m_coord = m2::PointD::Zero(); std::vector m_candidates; + size_t m_activePointIndex = kInvalidId; }; } // namespace impl @@ -54,21 +58,46 @@ private: class SegmentCorrespondence { public: + SegmentCorrespondence() = default; + + SegmentCorrespondence(SegmentCorrespondence const & sc) + { + m_partnerSegment = sc.m_partnerSegment; + + m_matchedPath = sc.m_matchedPath; + m_fakePath = sc.m_fakePath; + m_goldenPath = sc.m_goldenPath; + + m_partnerXMLSegment.reset(sc.m_partnerXMLSegment); + } + boost::optional const & GetMatchedPath() const { return m_matchedPath; } boost::optional & GetMatchedPath() { return m_matchedPath; } boost::optional const & GetFakePath() const { return m_fakePath; } boost::optional & GetFakePath() { return m_fakePath; } + boost::optional const & GetGoldenPath() const { return m_goldenPath; } + boost::optional & GetGoldenPath() { return m_goldenPath; } + openlr::LinearSegment const & GetPartnerSegment() const { return m_partnerSegment; } openlr::LinearSegment & GetPartnerSegment() { return m_partnerSegment; } uint32_t GetPartnerSegmentId() const { return m_partnerSegment.m_segmentId; } + pugi::xml_document const & GetPartnerXML() const { return m_partnerXMLSegment; } + void SetPartnerXML(pugi::xml_node const & n) { m_partnerXMLSegment.append_copy(n); } + private: openlr::LinearSegment m_partnerSegment; + // TODO(mgsergio): Maybe get rid of boost::optional and rely on emptiness of the path instead? boost::optional m_matchedPath; boost::optional m_fakePath; + boost::optional m_goldenPath; + + // A dirty hack to save back SegmentCorrespondence. + // TODO(mgsergio): Consider unifying xml serialization with one used in openlr_stat. + pugi::xml_document m_partnerXMLSegment; }; /// This class is used to delegate segments drawing to the DrapeEngine. @@ -154,17 +183,22 @@ public: public slots: void OnItemSelected(QItemSelection const & selected, QItemSelection const &); - void OnClick(m2::PointD const & clickPoint) { HandlePoint(clickPoint); } + void OnClick(m2::PointD const & clickPoint, Qt::MouseButton const button) + { + HandlePoint(clickPoint, button); + } private: - void HandlePoint(m2::PointD clickPoint); + void HandlePoint(m2::PointD clickPoint, Qt::MouseButton const button); Index const & m_index; std::vector m_segments; + // Non-owning pointer to an element of m_segments. + SegmentCorrespondence * m_currentSegment = nullptr; std::unique_ptr m_drawerDelegate; std::unique_ptr m_pointsDelegate; bool m_buildingPath = false; - std::vector m_path; + std::vector m_goldenPath; };