forked from organicmaps/organicmaps
Implement golden path saving.
This commit is contained in:
parent
36030dc944
commit
cf4c67525b
5 changed files with 166 additions and 62 deletions
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue