Implement golden path saving.

This commit is contained in:
Sergey Magidovich 2017-07-27 14:33:23 +03:00 committed by Yuri Gorshenin
parent 36030dc944
commit cf4c67525b
5 changed files with 166 additions and 62 deletions

View file

@ -67,7 +67,7 @@ public:
m_drapeApi.Clear();
}
void VisualizeGoldenPath(std::vector<m2::PointD> const & points)
void VisualizeGoldenPath(std::vector<m2::PointD> 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<TrafficDrawerDelegate>(m_framework),
make_unique<PointsControllerDelegate>(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);

View file

@ -2,6 +2,8 @@
#include "map/framework.hpp"
#include <QMouseEvent>
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());
}
}

View file

@ -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;

View file

@ -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 <QItemSelection>
#include <tuple>
namespace
{
std::vector<m2::PointD> 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<FeaturePoint> 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<m2::PointD> TrafficMode::GetCoordinates() const
{
std::vector<m2::PointD> 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"));

View file

@ -7,6 +7,8 @@
#include "base/exception.hpp"
#include "3party/pugixml/src/pugixml.hpp"
#include <cstdint>
#include <memory>
#include <string>
@ -15,6 +17,7 @@
#include <boost/optional.hpp>
#include <QAbstractTableModel>
#include <Qt>
class Index;
class QItemSelection;
@ -47,6 +50,7 @@ private:
m2::PointD m_coord = m2::PointD::Zero();
std::vector<FeaturePoint> 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<openlr::Path> const & GetMatchedPath() const { return m_matchedPath; }
boost::optional<openlr::Path> & GetMatchedPath() { return m_matchedPath; }
boost::optional<openlr::Path> const & GetFakePath() const { return m_fakePath; }
boost::optional<openlr::Path> & GetFakePath() { return m_fakePath; }
boost::optional<openlr::Path> const & GetGoldenPath() const { return m_goldenPath; }
boost::optional<openlr::Path> & 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<openlr::Path> m_matchedPath;
boost::optional<openlr::Path> m_fakePath;
boost::optional<openlr::Path> 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<SegmentCorrespondence> m_segments;
// Non-owning pointer to an element of m_segments.
SegmentCorrespondence * m_currentSegment = nullptr;
std::unique_ptr<TrafficDrawerDelegateBase> m_drawerDelegate;
std::unique_ptr<PointsControllerDelegateBase> m_pointsDelegate;
bool m_buildingPath = false;
std::vector<impl::RoadPointCandidate> m_path;
std::vector<impl::RoadPointCandidate> m_goldenPath;
};