[traff_assessment_tool] Move to tools

Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
mvglasow
2025-09-12 21:40:35 +03:00
parent e7bde9aa05
commit 07c75e627e
18 changed files with 10 additions and 13 deletions

View File

@@ -19,7 +19,3 @@ target_link_libraries(${PROJECT_NAME}
pugixml
coding
)
if (NOT SKIP_QT_GUI)
omim_add_tool_subdirectory(traff_assessment_tool)
endif()

View File

@@ -1,29 +0,0 @@
project(traff_assessment_tool)
set(SRC
main.cpp
mainwindow.cpp
mainwindow.hpp
map_widget.cpp
map_widget.hpp
points_controller_delegate_base.hpp
traffic_drawer_delegate_base.hpp
traffic_model.cpp
traffic_model.hpp
traffic_panel.cpp
traffic_panel.hpp
trafficmodeinitdlg.cpp
trafficmodeinitdlg.h
trafficmodeinitdlg.ui
)
omim_add_executable(${PROJECT_NAME} ${SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES AUTOUIC ON AUTOMOC ON)
target_link_libraries(${PROJECT_NAME}
qt_common
map
gflags::gflags
traffxml
)

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<plist>
<dict>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
</dict>
</plist>

View File

@@ -1,43 +0,0 @@
#include "mainwindow.hpp"
#include "qt/qt_common/helpers.hpp"
#include "map/framework.hpp"
#include <gflags/gflags.h>
#include <QtWidgets/QApplication>
namespace
{
DEFINE_string(resources_path, "", "Path to resources directory");
DEFINE_string(data_path, "", "Path to data directory");
} // namespace
int main(int argc, char * argv[])
{
gflags::SetUsageMessage("Visualize and check matched routes.");
gflags::ParseCommandLineFlags(&argc, &argv, true);
Platform & platform = GetPlatform();
if (!FLAGS_resources_path.empty())
platform.SetResourceDir(FLAGS_resources_path);
if (!FLAGS_data_path.empty())
platform.SetWritableDirForTests(FLAGS_data_path);
Q_INIT_RESOURCE(resources_common);
QApplication app(argc, argv);
qt::common::SetDefaultSurfaceFormat(app.platformName());
FrameworkParams params;
params.m_trafficTestMode = true;
Framework framework(params);
traffxml::MainWindow mainWindow(framework);
mainWindow.showMaximized();
return app.exec();
}

View File

@@ -1,477 +0,0 @@
#include "traffxml/traff_assessment_tool/mainwindow.hpp"
#include "traffxml/traff_assessment_tool/map_widget.hpp"
#include "traffxml/traff_assessment_tool/points_controller_delegate_base.hpp"
#include "traffxml/traff_assessment_tool/traffic_drawer_delegate_base.hpp"
#include "traffxml/traff_assessment_tool/traffic_panel.hpp"
#include "traffxml/traff_assessment_tool/trafficmodeinitdlg.h"
#include "map/framework.hpp"
#include "drape_frontend/drape_api.hpp"
#include "routing/data_source.hpp"
#include "routing/features_road_graph.hpp"
#include "routing/road_graph.hpp"
#include "routing_common/car_model.hpp"
#include "storage/country_parent_getter.hpp"
#include "traffxml/traff_model_xml.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include <QApplication>
#include <QClipboard>
#include <QDockWidget>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QKeySequence>
#include <QLayout>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QStandardPaths>
#include <cerrno>
#include <cstring>
#include <memory>
#include <vector>
namespace traffxml
{
namespace
{
class TrafficDrawerDelegate : public TrafficDrawerDelegateBase
{
static constexpr char const * kEncodedLineId = "encodedPath";
static constexpr char const * kDecodedLineId = "decodedPath";
static constexpr char const * kGoldenLineId = "goldenPath";
public:
explicit TrafficDrawerDelegate(Framework & framework)
: m_framework(framework)
, m_drapeApi(m_framework.GetDrapeApi())
, m_bm(framework.GetBookmarkManager())
{
}
void SetViewportCenter(m2::PointD const & center) override
{
m_framework.SetViewportCenter(center);
}
void DrawDecodedSegments(std::vector<m2::PointD> const & points) override
{
CHECK(!points.empty(), ("Points must not be empty."));
LOG(LINFO, ("Decoded segment", points));
m_drapeApi.AddLine(kDecodedLineId,
df::DrapeApiLineData(points, dp::Color(0, 0, 255, 255))
.Width(3.0f).ShowPoints(true /* markPoints */));
}
void DrawEncodedSegment(std::vector<m2::PointD> const & points) override
{
LOG(LINFO, ("Encoded segment", points));
m_drapeApi.AddLine(kEncodedLineId,
df::DrapeApiLineData(points, dp::Color(255, 0, 0, 255))
.Width(3.0f).ShowPoints(true /* markPoints */));
}
void DrawGoldenPath(std::vector<m2::PointD> const & points) override
{
m_drapeApi.AddLine(kGoldenLineId,
df::DrapeApiLineData(points, dp::Color(255, 127, 36, 255))
.Width(4.0f).ShowPoints(true /* markPoints */));
}
void ClearGoldenPath() override
{
m_drapeApi.RemoveLine(kGoldenLineId);
}
void ClearAllPaths() override
{
m_drapeApi.Clear();
}
void VisualizePoints(std::vector<m2::PointD> const & points) override
{
auto editSession = m_bm.GetEditSession();
editSession.SetIsVisible(UserMark::Type::DEBUG_MARK, true);
for (auto const & p : points)
editSession.CreateUserMark<DebugMarkPoint>(p);
}
void ClearAllVisualizedPoints() override
{
m_bm.GetEditSession().ClearGroup(UserMark::Type::DEBUG_MARK);
}
private:
Framework & m_framework;
df::DrapeApi & m_drapeApi;
BookmarkManager & m_bm;
};
bool PointsMatch(m2::PointD const & a, m2::PointD const & b)
{
auto constexpr kToleranceDistanceM = 1.0;
return mercator::DistanceOnEarth(a, b) < kToleranceDistanceM;
}
class PointsControllerDelegate : public PointsControllerDelegateBase
{
public:
explicit PointsControllerDelegate(Framework & framework)
: m_framework(framework)
, m_dataSource(const_cast<DataSource &>(GetDataSource()), nullptr /* numMwmIDs */)
, m_roadGraph(m_dataSource, routing::IRoadGraph::Mode::ObeyOnewayTag,
std::make_unique<routing::CarModelFactory>(storage::CountryParentGetter{}))
{
}
std::vector<m2::PointD> GetAllJunctionPointsInViewport() const override
{
std::vector<m2::PointD> points;
auto const & rect = m_framework.GetCurrentViewport();
auto const pushPoint = [&points, &rect](m2::PointD const & point)
{
if (!rect.IsPointInside(point))
return;
for (auto const & p : points)
{
if (PointsMatch(point, p))
return;
}
points.push_back(point);
};
auto const pushFeaturePoints = [&pushPoint](FeatureType & ft)
{
if (ft.GetGeomType() != feature::GeomType::Line)
return;
/// @todo Transported (railway=rail) are also present here :)
auto const roadClass = ftypes::GetHighwayClass(feature::TypesHolder(ft));
if (roadClass == ftypes::HighwayClass::Undefined ||
roadClass == ftypes::HighwayClass::Pedestrian)
{
return;
}
ft.ForEachPoint(pushPoint, scales::GetUpperScale());
};
GetDataSource().ForEachInRect(pushFeaturePoints, rect, scales::GetUpperScale());
return points;
}
std::pair<std::vector<FeaturePoint>, m2::PointD> GetCandidatePoints(
m2::PointD const & p) const override
{
auto constexpr kInvalidIndex = std::numeric_limits<size_t>::max();
std::vector<FeaturePoint> points;
m2::PointD pointOnFt;
indexer::ForEachFeatureAtPoint(GetDataSource(), [&points, &p, &pointOnFt](FeatureType & ft)
{
if (ft.GetGeomType() != feature::GeomType::Line)
return;
ft.ParseGeometry(FeatureType::BEST_GEOMETRY);
auto minDistance = std::numeric_limits<double>::max();
auto bestPointIndex = kInvalidIndex;
for (size_t i = 0; i < ft.GetPointsCount(); ++i)
{
auto const & fp = ft.GetPoint(i);
auto const distance = mercator::DistanceOnEarth(fp, p);
if (PointsMatch(fp, p) && distance < minDistance)
{
bestPointIndex = i;
minDistance = distance;
}
}
if (bestPointIndex != kInvalidIndex)
{
points.emplace_back(ft.GetID(), bestPointIndex);
pointOnFt = ft.GetPoint(bestPointIndex);
}
}, p);
return std::make_pair(points, pointOnFt);
}
std::vector<m2::PointD> GetReachablePoints(m2::PointD const & p) const override
{
routing::FeaturesRoadGraph::EdgeListT edges;
m_roadGraph.GetOutgoingEdges(geometry::PointWithAltitude(p, geometry::kDefaultAltitudeMeters),
edges);
std::vector<m2::PointD> points;
for (auto const & e : edges)
points.push_back(e.GetEndJunction().GetPoint());
return points;
}
ClickType CheckClick(m2::PointD const & clickPoint,
m2::PointD const & lastClickedPoint,
std::vector<m2::PointD> const & reachablePoints) const override
{
// == Comparison is safe here since |clickPoint| is adjusted by GetFeaturesPointsByPoint
// so to be equal the closest feature's one.
if (clickPoint == lastClickedPoint)
return ClickType::Remove;
for (auto const & p : reachablePoints)
{
if (PointsMatch(clickPoint, p))
return ClickType::Add;
}
return ClickType::Miss;
}
private:
DataSource const & GetDataSource() const { return m_framework.GetDataSource(); }
Framework & m_framework;
routing::MwmDataSource m_dataSource;
routing::FeaturesRoadGraph m_roadGraph;
};
} // namespace
MainWindow::MainWindow(Framework & framework)
: m_framework(framework)
{
m_mapWidget = new MapWidget(m_framework, this /* parent */);
m_layout = new QHBoxLayout();
m_layout->addWidget(m_mapWidget);
auto * window = new QWidget();
window->setLayout(m_layout);
window->setGraphicsEffect(nullptr);
setCentralWidget(window);
setWindowTitle(tr("TraFF Assessment Tool"));
setWindowIcon(QIcon(":/ui/logo.png"));
QMenu * fileMenu = new QMenu("File", this);
menuBar()->addMenu(fileMenu);
fileMenu->addAction("Open sample", QKeySequence("Ctrl+O"), this, &MainWindow::OnOpenTrafficSample);
fileMenu->addAction("Purge expired messages", QKeySequence("Ctrl+P"), this, &MainWindow::OnPurgeExpiredMessages);
fileMenu->addAction("Clear TraFF cache", QKeySequence("Ctrl+D"), this, &MainWindow::OnClearCache);
m_closeTrafficSampleAction = fileMenu->addAction("Close sample", QKeySequence("Ctrl+W"), this, &MainWindow::OnCloseTrafficSample);
m_saveTrafficSampleAction = fileMenu->addAction("Save sample", QKeySequence("Ctrl+S"), this, &MainWindow::OnSaveTrafficSample);
fileMenu->addSeparator();
#ifdef openlr_obsolete
m_goldifyMatchedPathAction = fileMenu->addAction("Goldify", QKeySequence("Ctrl+G"), [this] { m_trafficModel->GoldifyMatchedPath(); });
m_startEditingAction = fileMenu->addAction("Edit", QKeySequence("Ctrl+E"),
[this] {
m_trafficModel->StartBuildingPath();
m_mapWidget->SetMode(MapWidget::Mode::TrafficMarkup);
m_commitPathAction->setEnabled(true /* enabled */);
m_cancelPathAction->setEnabled(true /* enabled */);
});
m_commitPathAction = fileMenu->addAction("Accept path",
QKeySequence("Ctrl+A"),
[this] {
m_trafficModel->CommitPath();
m_mapWidget->SetMode(MapWidget::Mode::Normal);
});
m_cancelPathAction = fileMenu->addAction("Revert path",
QKeySequence("Ctrl+R"),
[this] {
m_trafficModel->RollBackPath();
m_mapWidget->SetMode(MapWidget::Mode::Normal);
});
m_ignorePathAction = fileMenu->addAction("Ignore path",
QKeySequence("Ctrl+I"),
[this] {
m_trafficModel->IgnorePath();
m_mapWidget->SetMode(MapWidget::Mode::Normal);
});
m_goldifyMatchedPathAction->setEnabled(false /* enabled */);
m_closeTrafficSampleAction->setEnabled(false /* enabled */);
m_saveTrafficSampleAction->setEnabled(false /* enabled */);
m_startEditingAction->setEnabled(false /* enabled */);
m_commitPathAction->setEnabled(false /* enabled */);
m_cancelPathAction->setEnabled(false /* enabled */);
m_ignorePathAction->setEnabled(false /* enabled */);
#endif
}
void MainWindow::CreateTrafficPanel()
{
if (!m_trafficModel)
{
// TODO simplify the call, everything depends on m_framework
m_trafficModel = new TrafficModel(m_framework, m_framework.GetDataSource(),
std::make_unique<TrafficDrawerDelegate>(m_framework),
std::make_unique<PointsControllerDelegate>(m_framework));
connect(m_mapWidget, &MapWidget::TrafficMarkupClick,
m_trafficModel, &TrafficModel::OnClick);
connect(m_trafficModel, &TrafficModel::EditingStopped,
this, &MainWindow::OnPathEditingStop);
connect(m_trafficModel, &TrafficModel::SegmentSelected,
[](int segmentId) { QApplication::clipboard()->setText(QString::number(segmentId)); });
}
if (!m_dockWidget)
{
m_dockWidget = new QDockWidget(tr("Messages"), this);
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_dockWidget);
m_dockWidget->setWidget(new TrafficPanel(m_trafficModel, m_dockWidget));
m_dockWidget->adjustSize();
m_dockWidget->setMinimumWidth(400);
}
m_dockWidget->show();
}
void MainWindow::DestroyTrafficPanel()
{
LOG(LINFO, ("enter"));
removeDockWidget(m_dockWidget);
delete m_dockWidget;
m_dockWidget = nullptr;
delete m_trafficModel;
m_trafficModel = nullptr;
m_mapWidget->SetMode(MapWidget::Mode::Normal);
}
void MainWindow::OnOpenTrafficSample()
{
TrafficModeInitDlg dlg;
dlg.exec();
if (dlg.result() != QDialog::DialogCode::Accepted)
return;
pugi::xml_document document;
LOG(LINFO, ("Attempting to load:", dlg.GetDataFilePath()));
auto const load_result = document.load_file(dlg.GetDataFilePath().data());
if (!load_result)
{
QMessageBox::critical(this, "Data loading error", QString::asprintf("Can't load file %s: %s", dlg.GetDataFilePath().data(), load_result.description()));
LOG(LERROR, ("Can't load file", dlg.GetDataFilePath(), ":", load_result.description()));
return;
}
std::setlocale(LC_ALL, "en_US.UTF-8");
traffxml::TraffFeed feed;
traffxml::TraffFeed shiftedFeed;
if (traffxml::ParseTraff(document, std::nullopt /* dataSource */, feed))
{
for (auto message : feed)
{
// `ShiftTimestamps()` will not change the message in `feed`, therefore construct a new feed
message.ShiftTimestamps();
shiftedFeed.push_back(message);
}
LOG(LINFO, ("TraFF data parsed successfully, pushing"));
m_framework.GetTrafficManager().ReceiveFeed(shiftedFeed);
LOG(LINFO, ("Push completed"));
}
else
{
QMessageBox::critical(this, "Data loading error", QString("An error occurred parsing the TraFF feed"));
LOG(LWARNING, ("An error occurred parsing the TraFF feed"));
return;
}
try
{
CreateTrafficPanel();
}
catch (TrafficModelError const & e)
{
QMessageBox::critical(this, "Data loading error", QString("Can't create traffic panel."));
LOG(LERROR, (e.Msg()));
return;
}
#ifdef openlr_obsolete
m_goldifyMatchedPathAction->setEnabled(true /* enabled */);
m_closeTrafficSampleAction->setEnabled(true /* enabled */);
m_saveTrafficSampleAction->setEnabled(true /* enabled */);
m_startEditingAction->setEnabled(true /* enabled */);
m_ignorePathAction->setEnabled(true /* enabled */);
#endif
}
void MainWindow::OnPurgeExpiredMessages()
{
m_framework.GetTrafficManager().PurgeExpiredMessages();
}
void MainWindow::OnClearCache()
{
m_framework.GetTrafficManager().Clear();
}
void MainWindow::OnCloseTrafficSample()
{
// TODO(mgsergio):
// If not saved, ask a user if he/she wants to save.
// OnSaveTrafficSample()
#ifdef openlr_obsolete
m_goldifyMatchedPathAction->setEnabled(false /* enabled */);
m_saveTrafficSampleAction->setEnabled(false /* enabled */);
m_closeTrafficSampleAction->setEnabled(false /* enabled */);
m_startEditingAction->setEnabled(false /* enabled */);
m_commitPathAction->setEnabled(false /* enabled */);
m_cancelPathAction->setEnabled(false /* enabled */);
m_ignorePathAction->setEnabled(false /* enabled */);
#endif
DestroyTrafficPanel();
}
void MainWindow::OnSaveTrafficSample()
{
// TODO(mgsergio): Add default filename.
auto const & fileName = QFileDialog::getSaveFileName(this, "Save sample");
if (fileName.isEmpty())
return;
pugi::xml_document document;
auto const messageCache = m_framework.GetTrafficManager().GetMessageCache();
traffxml::GenerateTraff(messageCache, document);
document.save_file(fileName.toStdString().data(), " " /* indent */);
#ifdef openlr_obsolete
if (!m_trafficModel->SaveSampleAs(fileName.toStdString()))
{
QMessageBox::critical(
this, "Saving error",
QString("Can't save file: ") + strerror(errno));
}
#endif
}
void MainWindow::OnPathEditingStop()
{
#ifdef openlr_obsolete
m_commitPathAction->setEnabled(false /* enabled */);
m_cancelPathAction->setEnabled(false /* enabled */);
m_cancelPathAction->setEnabled(false /* enabled */);
#endif
}
} // namespace traffxml

View File

@@ -1,77 +0,0 @@
#pragma once
#include "base/string_utils.hpp"
#include <string>
#include <QMainWindow>
class Framework;
class QHBoxLayout;
namespace traffxml
{
class MapWidget;
class TrafficModel;
class WebView;
}
namespace df
{
class DrapeApi;
}
class QDockWidget;
namespace traffxml
{
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(Framework & framework);
private:
void CreateTrafficPanel();
void DestroyTrafficPanel();
/**
* Called when the user requests to open a sample file.
*/
void OnOpenTrafficSample();
/**
* Called when the user requests to purge expired messages.
*/
void OnPurgeExpiredMessages();
/**
* Called when the user requests to clear the cache.
*/
void OnClearCache();
void OnCloseTrafficSample();
void OnSaveTrafficSample();
void OnPathEditingStop();
Framework & m_framework;
traffxml::TrafficModel * m_trafficModel = nullptr;
QDockWidget * m_dockWidget = nullptr;
#ifdef openlr_obsolete
QAction * m_goldifyMatchedPathAction = nullptr;
#endif
QAction * m_saveTrafficSampleAction = nullptr;
QAction * m_closeTrafficSampleAction = nullptr;
#ifdef openlr_obsolete
QAction * m_startEditingAction = nullptr;
QAction * m_commitPathAction = nullptr;
QAction * m_cancelPathAction = nullptr;
QAction * m_ignorePathAction = nullptr;
#endif
traffxml::MapWidget * m_mapWidget = nullptr;
QHBoxLayout * m_layout = nullptr;
};
} // namespace traffxml

View File

@@ -1,29 +0,0 @@
#include "traffxml/traff_assessment_tool/map_widget.hpp"
#include "qt/qt_common/helpers.hpp"
#include "map/framework.hpp"
#include <QMouseEvent>
namespace traffxml
{
MapWidget::MapWidget(Framework & framework, QWidget * parent)
: Base(framework, false /* screenshotMode */, parent)
{
}
void MapWidget::mousePressEvent(QMouseEvent * e)
{
Base::mousePressEvent(e);
if (qt::common::IsRightButton(e))
ShowInfoPopup(e, GetDevicePoint(e));
if (m_mode == Mode::TrafficMarkup)
{
auto pt = GetDevicePoint(e);
emit TrafficMarkupClick(m_framework.PtoG(pt), e->button());
}
}
} // namespace traffxml

View File

@@ -1,46 +0,0 @@
#pragma once
#include "qt/qt_common/map_widget.hpp"
namespace
{
class PointsController;
} // namespace
class Framework;
namespace traffxml
{
class MapWidget : public qt::common::MapWidget
{
Q_OBJECT
using Base = qt::common::MapWidget;
public:
enum class Mode
{
Normal,
TrafficMarkup
};
MapWidget(Framework & framework, QWidget * parent);
~MapWidget() override = default;
void SetMode(Mode const mode) { m_mode = mode; }
QSize sizeHint() const override
{
return QSize(800, 600);
}
signals:
void TrafficMarkupClick(m2::PointD const & p, Qt::MouseButton const b);
protected:
void mousePressEvent(QMouseEvent * e) override;
private:
Mode m_mode = Mode::Normal;
};
} // namespace traffxml

View File

@@ -1,40 +0,0 @@
#pragma once
#include "indexer/feature.hpp"
#include "geometry/point2d.hpp"
#include <cstddef>
#include <vector>
namespace traffxml
{
using FeaturePoint = std::pair<FeatureID, size_t>;
/// This class is responsible for collecting junction points and
/// checking user's clicks.
class PointsControllerDelegateBase
{
public:
enum class ClickType
{
Miss,
Add,
Remove
};
virtual ~PointsControllerDelegateBase() = default;
virtual std::vector<m2::PointD> GetAllJunctionPointsInViewport() const = 0;
/// Returns all junction points at a given location in the form of feature id and
/// point index in the feature.
virtual std::pair<std::vector<FeaturePoint>, m2::PointD> GetCandidatePoints(
m2::PointD const & p) const = 0;
// Returns all points that are one step reachable from |p|.
virtual std::vector<m2::PointD> GetReachablePoints(m2::PointD const & p) const = 0;
virtual ClickType CheckClick(m2::PointD const & clickPoint,
m2::PointD const & lastClickedPoint,
std::vector<m2::PointD> const & reachablePoints) const = 0;
};
} // namespace traffxml

View File

@@ -1,25 +0,0 @@
#pragma once
#include <geometry/point2d.hpp>
namespace traffxml
{
/// This class is used to delegate segments drawing to the DrapeEngine.
class TrafficDrawerDelegateBase
{
public:
virtual ~TrafficDrawerDelegateBase() = default;
virtual void SetViewportCenter(m2::PointD const & center) = 0;
virtual void DrawDecodedSegments(std::vector<m2::PointD> const & points) = 0;
virtual void DrawEncodedSegment(std::vector<m2::PointD> const & points) = 0;
virtual void DrawGoldenPath(std::vector<m2::PointD> const & points) = 0;
virtual void ClearGoldenPath() = 0;
virtual void ClearAllPaths() = 0;
virtual void VisualizePoints(std::vector<m2::PointD> const & points) = 0;
virtual void ClearAllVisualizedPoints() = 0;
};
} // namespace traffxml

View File

@@ -1,740 +0,0 @@
#include "traffic_model.hpp"
#ifdef openlr_obsolete
#include "openlr/openlr_model_xml.hpp"
#endif
#include "drape_frontend/drape_api.hpp"
#include "indexer/data_source.hpp"
#include "map/framework.hpp"
#include "base/assert.hpp"
#include "base/scope_guard.hpp"
#include <QItemSelection>
#include <QMessageBox>
namespace traffxml
{
constexpr static dp::Color kColorFrom(0x309302ff);
constexpr static dp::Color kColorAt(0x1a5ec1ff);
constexpr static dp::Color kColorVia(0xf19721ff);
constexpr static dp::Color kColorNotVia(0x8c5678ff);
constexpr static dp::Color kColorTo(0xe42300ff);
namespace
{
void RemovePointFromPull(m2::PointD const & toBeRemoved, std::vector<m2::PointD> & pool)
{
pool.erase(
remove_if(begin(pool), end(pool),
[&toBeRemoved](m2::PointD const & p) { return p.EqualDxDy(toBeRemoved, 1e-6); }),
end(pool));
}
std::vector<m2::PointD> GetReachablePoints(m2::PointD const & srcPoint,
std::vector<m2::PointD> const path,
PointsControllerDelegateBase const & pointsDelegate,
size_t const lookbackIndex)
{
auto reachablePoints = pointsDelegate.GetReachablePoints(srcPoint);
if (lookbackIndex < path.size())
{
auto const & toBeRemoved = path[path.size() - lookbackIndex - 1];
RemovePointFromPull(toBeRemoved, reachablePoints);
}
return reachablePoints;
}
} // namespace
#ifdef openlr_obsolete
namespace impl
{
// static
size_t const RoadPointCandidate::kInvalidId = std::numeric_limits<size_t>::max();
/// This class denotes a "non-deterministic" feature point.
/// I.e. it is a set of all pairs <FeatureID, point index>
/// located at a specified coordinate.
/// Only one point at a time is considered active.
RoadPointCandidate::RoadPointCandidate(std::vector<FeaturePoint> const & points,
m2::PointD const & coord)
: m_coord(coord)
, m_points(points)
{
LOG(LDEBUG, ("Candidate points:", points));
}
void RoadPointCandidate::ActivateCommonPoint(RoadPointCandidate const & rpc)
{
for (auto const & fp1 : m_points)
{
for (auto const & fp2 : rpc.m_points)
{
if (fp1.first == fp2.first)
{
SetActivePoint(fp1.first);
return;
}
}
}
CHECK(false, ("One common feature id should exist."));
}
FeaturePoint const & RoadPointCandidate::GetPoint() const
{
CHECK_NOT_EQUAL(m_activePointIndex, kInvalidId, ("No point is active."));
return m_points[m_activePointIndex];
}
m2::PointD const & RoadPointCandidate::GetCoordinate() const
{
return m_coord;
}
void RoadPointCandidate::SetActivePoint(FeatureID const & fid)
{
for (size_t i = 0; i < m_points.size(); ++i)
{
if (m_points[i].first == fid)
{
m_activePointIndex = i;
return;
}
}
CHECK(false, ("One point should match."));
}
} // namespace impl
#endif
QVariant GetCountryAndRoadRef(TraffMessage const & message)
{
std::string result = "";
if (message.m_location)
{
if (message.m_location.value().m_country)
result += message.m_location.value().m_country.value();
if (message.m_location.value().m_roadRef)
{
if (!result.empty())
result += '\n';
result += message.m_location.value().m_roadRef.value();
}
}
return QString::fromStdString(result);
}
/**
* @brief Returns a descriptive text for a point.
*
* The result is the junction name (if any), followed by the junction number or kilometric point (if any).
*
* @param point
* @return
*/
std::string GetPointDetail(Point const & point)
{
std::string result = point.m_junctionName ? point.m_junctionName.value() : "";
std::string junctionRefOrKmp = point.m_junctionRef ? point.m_junctionRef.value() :
point.m_distance ? std::format("km {0:.0f}", point.m_distance.value()) : "";
if (!junctionRefOrKmp.empty())
{
if (result.empty())
result = junctionRefOrKmp;
else
result += " (" + junctionRefOrKmp + ")";
}
return result;
}
/**
* @brief Returns a descriptive text for the location on the road.
*
* The result is one or two junctions, with an arrow to indicate direction where applicable.
*
* @param location
* @return
*/
std::string GetLocationDetail(TraffLocation const & location)
{
if (location.m_at)
return GetPointDetail(location.m_at.value());
std::string nameFrom = location.m_from ? GetPointDetail(location.m_from.value()) : "";
std::string nameTo = location.m_from ? GetPointDetail(location.m_to.value()) : "";
if (nameFrom == nameTo)
return nameFrom;
else if (!nameFrom.empty())
{
if (nameTo.empty())
return nameFrom + ((location.m_directionality == Directionality::OneDirection) ? "" : "");
else
return nameFrom
+ ((location.m_directionality == Directionality::OneDirection) ? "" : "")
+ nameTo;
}
else if (!nameTo.empty())
return ((location.m_directionality == Directionality::OneDirection) ? "" : "") + nameTo;
else if (location.m_via)
return GetPointDetail(location.m_via.value());
return "";
}
/**
* @brief Returns a description for the events in the message.
*
* @param message
* @return
*/
std::string GetEventText(TraffMessage const & message)
{
std::string result = "";
for (auto const & event : message.m_events)
{
if (!result.empty())
result += ", ";
result += DebugPrint(event.m_type);
// TODO quantifiers (format "q = {}")
// TODO supplementary information (not in struct yet)
if (event.m_length)
result += std::format(" for {0:d} m", event.m_length.value());
if (event.m_speed)
result += std::format(", speed {0:d} km/h", event.m_speed.value());
}
return result;
}
QVariant GetDescription(TraffMessage const & message)
{
std::string result = "";
if (message.m_cancellation)
{
result = "Cancellation";
}
else
{
if (message.m_location)
{
std::string direction = "";
if (message.m_location.value().m_directionality == Directionality::BothDirections)
direction = "both directions";
else
{
// TODO determine bearing and convert it to a string (northbound, southeastbound etc.)
/*
std::optional<Point> loc1 = message.m_location.value().m_from;
std::optional<Point> loc2 = message.m_location.value().m_to;
if (loc2 && !loc1)
loc1 = message.m_location.value().m_at;
else if (loc1 && !loc2)
loc2 = message.m_location.value().m_at;
if (loc1 && loc2)
{
ms::LatLon c1 = loc1.value().m_coordinates;
ms::LatLon c2 = loc2.value().m_coordinates;
// TODO figure out bearing (as string)
}
*/
}
if (message.m_location.value().m_roadName)
{
// roadName, town, direction
if (message.m_location.value().m_town)
result += message.m_location.value().m_town.value() + ", ";
// TODO territory?
result += message.m_location.value().m_roadName.value();
if (!direction.empty())
result += ", " + direction;
}
else if (message.m_location.value().m_origin && message.m_location.value().m_destination)
// origindestination with arrow
result += message.m_location.value().m_origin.value()
+ ((message.m_location.value().m_directionality == Directionality::BothDirections) ? "" : "")
+ message.m_location.value().m_destination.value();
else if (!message.m_location.value().m_origin && !message.m_location.value().m_destination)
// direction, if available
result += direction;
else if (message.m_location.value().m_directionality == Directionality::OneDirection)
{
// unidirectional, one endpoint; replacce the other with direction
if (message.m_location.value().m_origin)
result += message.m_location.value().m_origin.value() + "" + direction;
else
result += direction + "" + message.m_location.value().m_destination.value();
}
else
// direction, if available (no meaningful way to use origin or destination)
result += direction;
auto locationDetail = GetLocationDetail(message.m_location.value());
if (!locationDetail.empty())
{
if (!result.empty())
result += "\n";
result += locationDetail;
}
}
auto eventText = GetEventText(message);
if (!eventText.empty())
{
if (!result.empty())
result += "\n";
result += eventText;
}
// TODO start/end date
}
if (!result.empty())
result += "\n";
result += message.m_id.substr(0, message.m_id.find(':'));
result += "\t" + DebugPrint(message.m_updateTime);
// add an extra line to get bigger rows (default is too small)
result += "\n";
return QString::fromStdString(result);
}
// TrafficModel -------------------------------------------------------------------------------------
TrafficModel::TrafficModel(Framework & framework, DataSource const & dataSource,
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate, // TODO do we need that?
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate, // TODO do we need that?
QObject * parent)
: QAbstractTableModel(parent)
, m_framework(framework)
, m_dataSource(dataSource)
, m_drawerDelegate(std::move(drawerDelegate))
, m_pointsDelegate(std::move(pointsDelegate))
{
framework.GetTrafficManager().SetTrafficUpdateCallbackFn([this, &framework](bool final) {
/*
* If final is true, this indicates the queue has been emptied and no further updates are
* imminent. Such updates should always be processed. If final is false, we can optimize by
* selectively skipping updates.
*/
GetPlatform().RunTask(Platform::Thread::Gui, [this, &framework]()
{
beginResetModel();
auto const messageCache = framework.GetTrafficManager().GetMessageCache();
m_messages.clear();
m_messages.reserve(messageCache.size());
for (auto & entry : messageCache)
m_messages.push_back(std::move(entry.second));
endResetModel();
LOG(LINFO, ("Messages:", m_messages.size()));
});
});
}
// TODO(mgsergio): Check if a path was committed, or commit it.
bool TrafficModel::SaveSampleAs(std::string const & fileName) const
{
CHECK(!fileName.empty(), ("Can't save to an empty file."));
pugi::xml_document result;
result.reset(m_template);
auto root = result.document_element();
#ifdef openlr_obsolete
for (auto const & sc : m_segments)
{
auto segment = root.append_child("Segment");
segment.append_copy(sc.GetPartnerXMLSegment());
if (sc.GetStatus() == SegmentCorrespondence::Status::Ignored)
{
segment.append_child("Ignored").text() = true;
}
if (sc.HasMatchedPath())
{
auto node = segment.append_child("Route");
openlr::PathToXML(sc.GetMatchedPath(), node);
}
if (sc.HasFakePath())
{
auto node = segment.append_child("FakeRoute");
openlr::PathToXML(sc.GetFakePath(), node);
}
if (sc.HasGoldenPath())
{
auto node = segment.append_child("GoldenRoute");
openlr::PathToXML(sc.GetGoldenPath(), node);
}
}
#endif
result.save_file(fileName.data(), " " /* indent */);
return true;
}
int TrafficModel::rowCount(const QModelIndex & parent) const
{
return static_cast<int>(m_messages.size());
}
int TrafficModel::columnCount(const QModelIndex & parent) const { return 2; }
QVariant TrafficModel::data(const QModelIndex & index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= rowCount())
return QVariant();
if (role != Qt::DisplayRole && role != Qt::EditRole)
return QVariant();
#ifdef openlr_obsolete
if (index.column() == 0)
return m_segments[index.row()].GetPartnerSegmentId();
if (index.column() == 1)
return static_cast<int>(m_segments[index.row()].GetStatus());
if (index.column() == 2)
return m_segments[index.row()].GetPositiveOffset();
if (index.column() == 3)
return m_segments[index.row()].GetNegativeOffset();
#endif
switch (index.column())
{
case 0:
return GetCountryAndRoadRef(m_messages[index.row()]);
case 1:
return GetDescription(m_messages[index.row()]);
default:
return QVariant();
}
UNREACHABLE();
}
QVariant TrafficModel::headerData(int section, Qt::Orientation orientation,
int role /* = Qt::DisplayRole */) const
{
/*
* Qt seems buggy here. Initially, we seem to get called with Qt::Vertical for a horizontal
* header, i.e. a row of column headers, and Qt::Horizontal for a vertical header (column of row
* headers). Using the intuitively correct value will result in incorrect behavior and a lot of
* head-scratching if you use just one type of header.
* However, this (presumed) bug does not seem to be consistent, as updates call us with
* Qt::Vertical and a row number (which can be beyond the number of columns).
*/
if (orientation == Qt::Horizontal && role != Qt::DisplayRole)
return QVariant();
switch (section)
{
case 0: return "Road ref"; break;
case 1: return "Description"; break;
}
return QVariant();
}
void TrafficModel::OnItemSelected(QItemSelection const & selected, QItemSelection const &)
{
ASSERT(!selected.empty(), ());
ASSERT(!m_messages.empty(), ());
auto const row = selected.front().top();
CHECK_LESS(static_cast<size_t>(row), m_messages.size(), ());
auto message = &m_messages[row];
if (!message->m_location)
return;
m2::RectD rect;
auto editSession = m_framework.GetBookmarkManager().GetEditSession();
editSession.ClearGroup(UserMark::Type::COLORED);
editSession.SetIsVisible(UserMark::Type::COLORED, true);
for (auto & [coords, color] : {
std::pair{message->m_location.value().m_from, kColorFrom},
std::pair{message->m_location.value().m_at, kColorAt},
std::pair{message->m_location.value().m_via, kColorVia},
std::pair{message->m_location.value().m_notVia, kColorNotVia},
std::pair{message->m_location.value().m_to, kColorTo}
})
if (coords)
{
auto point = mercator::FromLatLon(coords.value().m_coordinates);
rect.Add(point);
auto mark = editSession.CreateUserMark<ColoredMarkPoint>(point);
mark->SetColor(color);
}
if (rect.IsValid())
{
rect.Scale(1.5);
m_framework.ShowRect(rect, 15 /* maxScale */, true /* animation */, true /* useVisibleViewport */);
}
}
Qt::ItemFlags TrafficModel::flags(QModelIndex const & index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index);
}
#ifdef openlr_obsolete
void TrafficModel::GoldifyMatchedPath()
{
if (!m_currentSegment->HasMatchedPath())
{
QMessageBox::information(nullptr /* parent */, "Error",
"The selected segment does not have a matched path");
return;
}
if (!StartBuildingPathChecks())
return;
m_currentSegment->SetGoldenPath(m_currentSegment->GetMatchedPath());
m_goldenPath.clear();
m_drawerDelegate->DrawGoldenPath(GetPoints(m_currentSegment->GetGoldenPath()));
}
void TrafficModel::StartBuildingPath()
{
if (!StartBuildingPathChecks())
return;
m_currentSegment->SetGoldenPath({});
m_buildingPath = true;
m_drawerDelegate->ClearGoldenPath();
m_drawerDelegate->VisualizePoints(m_pointsDelegate->GetAllJunctionPointsInViewport());
}
void TrafficModel::PushPoint(m2::PointD const & coord, std::vector<FeaturePoint> const & points)
{
impl::RoadPointCandidate point(points, coord);
if (!m_goldenPath.empty())
m_goldenPath.back().ActivateCommonPoint(point);
m_goldenPath.push_back(point);
}
void TrafficModel::PopPoint()
{
CHECK(!m_goldenPath.empty(), ("Attempt to pop point from an empty path."));
m_goldenPath.pop_back();
}
void TrafficModel::CommitPath()
{
CHECK(m_currentSegment, ("No segments selected"));
if (!m_buildingPath)
MYTHROW(TrafficModelError, ("Path building is not started"));
SCOPE_GUARD(guard, [this] { emit EditingStopped(); });
m_buildingPath = false;
m_drawerDelegate->ClearAllVisualizedPoints();
if (m_goldenPath.size() == 1)
{
LOG(LDEBUG, ("Golden path is empty"));
return;
}
CHECK_GREATER(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().ActivateCommonPoint(m_goldenPath[GetPointsCount() - 2]);
openlr::Path path;
for (size_t i = 1; i < GetPointsCount(); ++i)
{
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.ActivateCommonPoint(prevPoint);
auto const & prevFt = prevPoint.GetPoint();
auto const & ft = point.GetPoint();
path.push_back(Edge::MakeReal(
ft.first, prevFt.second < ft.second /* forward */, base::checked_cast<uint32_t>(prevFt.second),
geometry::PointWithAltitude(prevPoint.GetCoordinate(), 0 /* altitude */),
geometry::PointWithAltitude(point.GetCoordinate(), 0 /* altitude */)));
}
m_currentSegment->SetGoldenPath(path);
m_goldenPath.clear();
}
void TrafficModel::RollBackPath()
{
CHECK(m_currentSegment, ("No segments selected"));
CHECK(m_buildingPath, ("No path building is in progress."));
m_buildingPath = false;
// TODO(mgsergio): Add a method for common visual manipulations.
m_drawerDelegate->ClearAllVisualizedPoints();
m_drawerDelegate->ClearGoldenPath();
if (m_currentSegment->HasGoldenPath())
m_drawerDelegate->DrawGoldenPath(GetPoints(m_currentSegment->GetGoldenPath()));
m_goldenPath.clear();
emit EditingStopped();
}
void TrafficModel::IgnorePath()
{
CHECK(m_currentSegment, ("No segments selected"));
if (m_currentSegment->HasGoldenPath())
{
auto const btn =
QMessageBox::question(nullptr /* parent */, "Override warning",
"The selected segment has a golden path. Do you want to discard it?");
if (btn == QMessageBox::No)
return;
}
m_buildingPath = false;
// TODO(mgsergio): Add a method for common visual manipulations.
m_drawerDelegate->ClearAllVisualizedPoints();
m_drawerDelegate->ClearGoldenPath();
m_currentSegment->Ignore();
m_goldenPath.clear();
emit EditingStopped();
}
size_t TrafficModel::GetPointsCount() const
{
return m_goldenPath.size();
}
m2::PointD const & TrafficModel::GetPoint(size_t const index) const
{
return m_goldenPath[index].GetCoordinate();
}
m2::PointD const & TrafficModel::GetLastPoint() const
{
CHECK(!m_goldenPath.empty(), ("Attempt to get point from an empty path."));
return m_goldenPath.back().GetCoordinate();
}
std::vector<m2::PointD> TrafficModel::GetGoldenPathPoints() const
{
std::vector<m2::PointD> coordinates;
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 TrafficModel::HandlePoint(m2::PointD clickPoint, Qt::MouseButton const button)
{
if (!m_buildingPath)
return;
auto const currentPathLength = GetPointsCount();
auto const lastClickedPoint = currentPathLength != 0
? GetLastPoint()
: m2::PointD::Zero();
auto const & p = m_pointsDelegate->GetCandidatePoints(clickPoint);
auto const & candidatePoints = p.first;
clickPoint = p.second;
if (candidatePoints.empty())
return;
auto reachablePoints = GetReachablePoints(clickPoint, GetGoldenPathPoints(), *m_pointsDelegate,
0 /* lookBackIndex */);
auto const & clickablePoints = currentPathLength != 0
? GetReachablePoints(lastClickedPoint, GetGoldenPathPoints(), *m_pointsDelegate,
1 /* lookbackIndex */)
// TODO(mgsergio): This is not quite correct since view port can change
// since first call to visualize points. But it's ok in general.
: m_pointsDelegate->GetAllJunctionPointsInViewport();
using ClickType = PointsControllerDelegateBase::ClickType;
switch (m_pointsDelegate->CheckClick(clickPoint, lastClickedPoint, clickablePoints))
{
case ClickType::Add:
// TODO(mgsergio): Think of refactoring this with if (accumulator.empty)
// instead of pushing point first ad then removing last selection.
PushPoint(clickPoint, candidatePoints);
if (currentPathLength > 0)
{
// TODO(mgsergio): Should I remove lastClickedPoint from clickablePoints
// as well?
RemovePointFromPull(lastClickedPoint, reachablePoints);
m_drawerDelegate->DrawGoldenPath(GetGoldenPathPoints());
}
m_drawerDelegate->ClearAllVisualizedPoints();
m_drawerDelegate->VisualizePoints(reachablePoints);
m_drawerDelegate->VisualizePoints({clickPoint});
break;
case ClickType::Remove: // TODO(mgsergio): Rename this case.
if (button == Qt::MouseButton::LeftButton) // RemovePoint
{
m_drawerDelegate->ClearAllVisualizedPoints();
m_drawerDelegate->ClearGoldenPath();
PopPoint();
if (m_goldenPath.empty())
{
m_drawerDelegate->VisualizePoints(m_pointsDelegate->GetAllJunctionPointsInViewport());
}
else
{
m_drawerDelegate->VisualizePoints(GetReachablePoints(
GetLastPoint(), GetGoldenPathPoints(), *m_pointsDelegate, 1 /* lookBackIndex */));
}
if (GetPointsCount() > 1)
m_drawerDelegate->DrawGoldenPath(GetGoldenPathPoints());
}
else if (button == Qt::MouseButton::RightButton)
{
CommitPath();
}
break;
case ClickType::Miss:
// TODO(mgsergio): This situation should be handled by checking candidatePoitns.empty() above.
// Not shure though if all cases are handled by that check.
return;
}
}
bool TrafficModel::StartBuildingPathChecks() const
{
CHECK(m_currentSegment, ("A segment should be selected before path building is started."));
if (m_buildingPath)
MYTHROW(TrafficModelError, ("Path building already in progress."));
if (m_currentSegment->HasGoldenPath())
{
auto const btn = QMessageBox::question(
nullptr /* parent */, "Override warning",
"The selected segment already has a golden path. Do you want to override?");
if (btn == QMessageBox::No)
return false;
}
return true;
}
#endif
} // namespace traffxml

View File

@@ -1,149 +0,0 @@
#pragma once
#include "points_controller_delegate_base.hpp"
#ifdef openlr_obsolete
#include "segment_correspondence.hpp"
#endif
#include "traffic_drawer_delegate_base.hpp"
#ifdef openlr_obsolete
#include "openlr/decoded_path.hpp"
#endif
#include "traffxml/traff_model.hpp"
#include "indexer/data_source.hpp"
#include "base/exception.hpp"
#include <pugixml.hpp>
#include <memory>
#include <string>
#include <vector>
#include <QAbstractTableModel>
class QItemSelection;
class Selection;
DECLARE_EXCEPTION(TrafficModelError, RootException);
namespace traffxml
{
#ifdef openlr_obsolete
namespace impl
{
/// This class denotes a "non-deterministic" feature point.
/// I.e. it is a set of all pairs <FeatureID, point index>
/// located at a specified coordinate.
/// Only one point at a time is considered active.
class RoadPointCandidate
{
public:
RoadPointCandidate(std::vector<openlr::FeaturePoint> const & points,
m2::PointD const & coord);
void ActivateCommonPoint(RoadPointCandidate const & rpc);
openlr::FeaturePoint const & GetPoint() const;
m2::PointD const & GetCoordinate() const;
private:
static size_t const kInvalidId;
void SetActivePoint(FeatureID const & fid);
m2::PointD m_coord = m2::PointD::Zero();
std::vector<openlr::FeaturePoint> m_points;
size_t m_activePointIndex = kInvalidId;
};
} // namespace impl
#endif
/// This class is used to map sample ids to real data
/// and change sample evaluations.
class TrafficModel : public QAbstractTableModel
{
Q_OBJECT
public:
// TODO(mgsergio): Check we are on the right mwm. I.e. right mwm version and everything.
TrafficModel(Framework & framework, DataSource const & dataSource,
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate,
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate,
QObject * parent = Q_NULLPTR);
bool SaveSampleAs(std::string const & fileName) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex & parent = QModelIndex()) const Q_DECL_OVERRIDE;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex & index, int role) const Q_DECL_OVERRIDE;
Qt::ItemFlags flags(QModelIndex const & index) const Q_DECL_OVERRIDE;
bool IsBuildingPath() const { return m_buildingPath; }
#ifdef openlr_obsolete
void GoldifyMatchedPath();
void StartBuildingPath();
void PushPoint(m2::PointD const & coord,
std::vector<FeaturePoint> const & points);
void PopPoint();
void CommitPath();
void RollBackPath();
void IgnorePath();
size_t GetPointsCount() const;
m2::PointD const & GetPoint(size_t const index) const;
m2::PointD const & GetLastPoint() const;
std::vector<m2::PointD> GetGoldenPathPoints() const;
#endif
public slots:
void OnItemSelected(QItemSelection const & selected, QItemSelection const &);
void OnClick(m2::PointD const & clickPoint, Qt::MouseButton const button)
{
#ifdef openlr_obsolete
HandlePoint(clickPoint, button);
#endif
}
signals:
void EditingStopped();
void SegmentSelected(int segmentId);
private:
#ifdef openlr_obsolete
void HandlePoint(m2::PointD clickPoint, Qt::MouseButton const button);
bool StartBuildingPathChecks() const;
#endif
Framework & m_framework;
DataSource const & m_dataSource;
#ifdef openlr_obsolete
std::vector<SegmentCorrespondence> m_segments;
// Non-owning pointer to an element of m_segments.
SegmentCorrespondence * m_currentSegment = nullptr;
#endif
std::vector<TraffMessage> m_messages;
/**
* Non-owning pointer to an element of m_messages.
*/
TraffMessage * m_message = nullptr;
std::unique_ptr<TrafficDrawerDelegateBase> m_drawerDelegate;
std::unique_ptr<PointsControllerDelegateBase> m_pointsDelegate;
bool m_buildingPath = false;
#ifdef openlr_obsolete
std::vector<impl::RoadPointCandidate> m_goldenPath;
#endif
// Clone this document and add things to its clone when saving sample.
pugi::xml_document m_template;
};
} // namespace traffxml

View File

@@ -1,89 +0,0 @@
#include "traffxml/traff_assessment_tool/traffic_panel.hpp"
#include <QtCore/QAbstractTableModel>
#include <QtWidgets/QBoxLayout>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QTableView>
namespace traffxml
{
// ComboBoxDelegate --------------------------------------------------------------------------------
ComboBoxDelegate::ComboBoxDelegate(QObject * parent)
: QStyledItemDelegate(parent)
{
}
QWidget * ComboBoxDelegate::createEditor(QWidget * parent, QStyleOptionViewItem const & option,
QModelIndex const & index) const
{
auto * editor = new QComboBox(parent);
editor->setFrame(false);
editor->setEditable(false);
editor->addItems({"Unevaluated", "Positive", "Negative", "RelPositive", "RelNegative", "Ignore"});
return editor;
}
void ComboBoxDelegate::setEditorData(QWidget * editor, QModelIndex const & index) const
{
auto const value = index.model()->data(index, Qt::EditRole).toString();
static_cast<QComboBox*>(editor)->setCurrentText(value);
}
void ComboBoxDelegate::setModelData(QWidget * editor, QAbstractItemModel * model,
QModelIndex const & index) const
{
model->setData(index, static_cast<QComboBox*>(editor)->currentText(), Qt::EditRole);
}
void ComboBoxDelegate::updateEditorGeometry(QWidget * editor, QStyleOptionViewItem const & option,
QModelIndex const & index) const
{
editor->setGeometry(option.rect);
}
// TrafficPanel ------------------------------------------------------------------------------------
TrafficPanel::TrafficPanel(QAbstractItemModel * trafficModel, QWidget * parent)
: QWidget(parent)
{
CreateTable(trafficModel);
auto * layout = new QVBoxLayout();
layout->addWidget(m_table);
setLayout(layout);
// Select first segment by default;
auto const & index = m_table->model()->index(0, 0);
m_table->selectionModel()->select(index, QItemSelectionModel::Select);
}
void TrafficPanel::CreateTable(QAbstractItemModel * trafficModel)
{
m_table = new QTableView();
m_table->setFocusPolicy(Qt::NoFocus);
m_table->setAlternatingRowColors(true);
m_table->setShowGrid(false);
m_table->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
m_table->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
m_table->setModel(trafficModel);
m_table->setItemDelegate(new ComboBoxDelegate());
// the model must be set before we can set dimensions and headers
m_table->verticalHeader()->setVisible(false);
m_table->horizontalHeader()->setVisible(true);
//m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
m_table->setColumnWidth(0, 80);
m_table->setColumnWidth(1, 300);
connect(m_table->selectionModel(),
SIGNAL(selectionChanged(QItemSelection const &, QItemSelection const &)),
trafficModel, SLOT(OnItemSelected(QItemSelection const &, QItemSelection const &)));
connect(trafficModel, &QAbstractItemModel::modelReset,
m_table, [this]() {
m_table->resizeRowsToContents();
//m_table->resizeColumnsToContents();
});
}
} // namespace traffxml

View File

@@ -1,50 +0,0 @@
#pragma once
#include <QtWidgets/QStyledItemDelegate>
class QAbstractItemModel;
class QComboBox;
class QTableView;
class QWidget;
namespace traffxml
{
class ComboBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
ComboBoxDelegate(QObject * parent = 0);
QWidget * createEditor(QWidget * parent, QStyleOptionViewItem const & option,
QModelIndex const & index) const Q_DECL_OVERRIDE;
void setEditorData(QWidget * editor, QModelIndex const & index) const Q_DECL_OVERRIDE;
void setModelData(QWidget * editor, QAbstractItemModel * model,
QModelIndex const & index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget * editor, QStyleOptionViewItem const & option,
QModelIndex const & index) const Q_DECL_OVERRIDE;
};
class TrafficPanel : public QWidget
{
Q_OBJECT
public:
explicit TrafficPanel(QAbstractItemModel * trafficModel, QWidget * parent);
private:
void CreateTable(QAbstractItemModel * trafficModel);
void FillTable();
signals:
public slots:
// void OnCheckBoxClicked(int row, int state);
private:
QTableView * m_table = Q_NULLPTR;
};
} // namespace traffxml

View File

@@ -1,59 +0,0 @@
#include "traffxml/traff_assessment_tool/trafficmodeinitdlg.h"
#include "ui_trafficmodeinitdlg.h"
#include "platform/settings.hpp"
#include <QtWidgets/QFileDialog>
#include <QFileInfo>
#include <string>
namespace
{
std::string const kDataFilePath = "LastTraffAssessmentDataFilePath";
} // namespace
namespace traffxml
{
TrafficModeInitDlg::TrafficModeInitDlg(QWidget * parent) :
QDialog(parent),
m_ui(new Ui::TrafficModeInitDlg)
{
m_ui->setupUi(this);
QString directory = {};
std::string lastDataFilePath;
if (settings::Get(kDataFilePath, lastDataFilePath))
{
m_ui->dataFileName->setText(QString::fromStdString(lastDataFilePath));
directory = QFileInfo(QString::fromStdString(lastDataFilePath)).absolutePath();
}
connect(m_ui->chooseDataFileButton, &QPushButton::clicked, [this, directory](bool) {
SetFilePathViaDialog(*m_ui->dataFileName, tr("Choose data file"), directory, "*.xml");
});
}
TrafficModeInitDlg::~TrafficModeInitDlg()
{
delete m_ui;
}
void TrafficModeInitDlg::accept()
{
m_dataFileName = m_ui->dataFileName->text().trimmed().toStdString();
settings::Set(kDataFilePath, m_dataFileName);
QDialog::accept();
}
void TrafficModeInitDlg::SetFilePathViaDialog(QLineEdit & dest, QString const & title,
QString const & directory, QString const & filter)
{
QFileDialog openFileDlg(nullptr, title, directory, filter);
openFileDlg.exec();
if (openFileDlg.result() != QDialog::DialogCode::Accepted)
return;
dest.setText(openFileDlg.selectedFiles().first());
}
} // namespace traffxml

View File

@@ -1,36 +0,0 @@
#pragma once
#include <string>
#include <QtWidgets/QDialog>
class QLineEdit;
namespace Ui {
class TrafficModeInitDlg;
}
namespace traffxml
{
class TrafficModeInitDlg : public QDialog
{
Q_OBJECT
public:
explicit TrafficModeInitDlg(QWidget * parent = nullptr);
~TrafficModeInitDlg();
std::string GetDataFilePath() const { return m_dataFileName; }
private:
void SetFilePathViaDialog(QLineEdit & dest, QString const & title, const QString & directory,
QString const & filter = {});
public slots:
void accept() override;
private:
Ui::TrafficModeInitDlg * m_ui;
std::string m_dataFileName;
};
} // namespace traffxml

View File

@@ -1,111 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TrafficModeInitDlg</class>
<widget class="QDialog" name="TrafficModeInitDlg">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>122</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Select traffic files</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>10</y>
<width>441</width>
<height>101</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="chooseDataFileButton">
<property name="text">
<string>Choose...</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="okButton">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="dataFileName"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="dataLabel">
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Data file:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>dataFileName</tabstop>
<tabstop>chooseDataFileButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>cancelButton</sender>
<signal>clicked()</signal>
<receiver>TrafficModeInitDlg</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>290</x>
<y>103</y>
</hint>
<hint type="destinationlabel">
<x>164</x>
<y>98</y>
</hint>
</hints>
</connection>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>TrafficModeInitDlg</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>405</x>
<y>105</y>
</hint>
<hint type="destinationlabel">
<x>59</x>
<y>94</y>
</hint>
</hints>
</connection>
</connections>
</ui>