mirror of
https://codeberg.org/comaps/comaps
synced 2026-01-06 12:34:24 +00:00
[traff_assessment_tool] Basic traffic message panel
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -170,6 +170,11 @@ void TrafficManager::Clear()
|
|||||||
OnTrafficDataUpdate();
|
OnTrafficDataUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrafficManager::SetTrafficUpdateCallbackFn(TrafficUpdateCallbackFn && fn)
|
||||||
|
{
|
||||||
|
m_trafficUpdateCallbackFn = std::move(fn);
|
||||||
|
}
|
||||||
|
|
||||||
void TrafficManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
|
void TrafficManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
|
||||||
{
|
{
|
||||||
m_drapeEngine.Set(engine);
|
m_drapeEngine.Set(engine);
|
||||||
@@ -815,6 +820,9 @@ void TrafficManager::OnTrafficDataUpdate()
|
|||||||
m_lastStorageUpdate = steady_clock::now();
|
m_lastStorageUpdate = steady_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_trafficUpdateCallbackFn)
|
||||||
|
m_trafficUpdateCallbackFn.value()(feedQueueEmpty);
|
||||||
|
|
||||||
if (!notifyDrape && !notifyObserver)
|
if (!notifyDrape && !notifyObserver)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class TrafficManager final : public traffxml::TraffSourceManager
|
|||||||
public:
|
public:
|
||||||
using CountryInfoGetterFn = std::function<storage::CountryInfoGetter const &()>;
|
using CountryInfoGetterFn = std::function<storage::CountryInfoGetter const &()>;
|
||||||
using CountryParentNameGetterFn = std::function<std::string(std::string const &)>;
|
using CountryParentNameGetterFn = std::function<std::string(std::string const &)>;
|
||||||
|
using TrafficUpdateCallbackFn = std::function<void(bool)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Global state of traffic information.
|
* @brief Global state of traffic information.
|
||||||
@@ -303,6 +304,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a callback function which gets called on traffic updates.
|
||||||
|
*
|
||||||
|
* Intended for testing.
|
||||||
|
*
|
||||||
|
* @param fn The callback function.
|
||||||
|
*/
|
||||||
|
void SetTrafficUpdateCallbackFn(TrafficUpdateCallbackFn && fn);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -682,6 +692,13 @@ private:
|
|||||||
* thread).
|
* thread).
|
||||||
*/
|
*/
|
||||||
std::map<MwmSet::MwmId, traffic::TrafficInfo::Coloring> m_allMwmColoring;
|
std::map<MwmSet::MwmId, traffic::TrafficInfo::Coloring> m_allMwmColoring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback function which gets called on traffic updates.
|
||||||
|
*
|
||||||
|
* Intended for testing.
|
||||||
|
*/
|
||||||
|
std::optional<TrafficUpdateCallbackFn> m_trafficUpdateCallbackFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::string DebugPrint(TrafficManager::TrafficState state);
|
extern std::string DebugPrint(TrafficManager::TrafficState state);
|
||||||
|
|||||||
@@ -311,35 +311,42 @@ MainWindow::MainWindow(Framework & framework)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::CreateTrafficPanel(std::string const & dataFilePath)
|
void MainWindow::CreateTrafficPanel()
|
||||||
{
|
{
|
||||||
m_trafficModel = new TrafficModel(dataFilePath,
|
if (!m_trafficModel)
|
||||||
m_framework.GetDataSource(),
|
{
|
||||||
std::make_unique<TrafficDrawerDelegate>(m_framework),
|
// TODO simplify the call, everything depends on m_framework
|
||||||
std::make_unique<PointsControllerDelegate>(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,
|
connect(m_mapWidget, &MapWidget::TrafficMarkupClick,
|
||||||
m_trafficModel, &TrafficModel::OnClick);
|
m_trafficModel, &TrafficModel::OnClick);
|
||||||
connect(m_trafficModel, &TrafficModel::EditingStopped,
|
connect(m_trafficModel, &TrafficModel::EditingStopped,
|
||||||
this, &MainWindow::OnPathEditingStop);
|
this, &MainWindow::OnPathEditingStop);
|
||||||
connect(m_trafficModel, &TrafficModel::SegmentSelected,
|
connect(m_trafficModel, &TrafficModel::SegmentSelected,
|
||||||
[](int segmentId) { QApplication::clipboard()->setText(QString::number(segmentId)); });
|
[](int segmentId) { QApplication::clipboard()->setText(QString::number(segmentId)); });
|
||||||
|
}
|
||||||
|
|
||||||
m_docWidget = new QDockWidget(tr("Routes"), this);
|
if (!m_dockWidget)
|
||||||
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_docWidget);
|
{
|
||||||
|
m_dockWidget = new QDockWidget(tr("Messages"), this);
|
||||||
|
addDockWidget(Qt::DockWidgetArea::RightDockWidgetArea, m_dockWidget);
|
||||||
|
|
||||||
m_docWidget->setWidget(new TrafficPanel(m_trafficModel, m_docWidget));
|
m_dockWidget->setWidget(new TrafficPanel(m_trafficModel, m_dockWidget));
|
||||||
|
|
||||||
m_docWidget->adjustSize();
|
m_dockWidget->adjustSize();
|
||||||
m_docWidget->setMinimumWidth(400);
|
m_dockWidget->setMinimumWidth(400);
|
||||||
m_docWidget->show();
|
}
|
||||||
|
m_dockWidget->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::DestroyTrafficPanel()
|
void MainWindow::DestroyTrafficPanel()
|
||||||
{
|
{
|
||||||
removeDockWidget(m_docWidget);
|
LOG(LINFO, ("enter"));
|
||||||
delete m_docWidget;
|
removeDockWidget(m_dockWidget);
|
||||||
m_docWidget = nullptr;
|
delete m_dockWidget;
|
||||||
|
m_dockWidget = nullptr;
|
||||||
|
|
||||||
delete m_trafficModel;
|
delete m_trafficModel;
|
||||||
m_trafficModel = nullptr;
|
m_trafficModel = nullptr;
|
||||||
@@ -386,19 +393,16 @@ void MainWindow::OnOpenTrafficSample()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO create traffic panel with TraFF messages
|
|
||||||
#if 0
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CreateTrafficPanel(dlg.GetDataFilePath());
|
CreateTrafficPanel();
|
||||||
}
|
}
|
||||||
catch (TrafficModelError const & e)
|
catch (TrafficModelError const & e)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, "Data loading error", QString("Can't load data file."));
|
QMessageBox::critical(this, "Data loading error", QString("Can't create traffic panel."));
|
||||||
LOG(LERROR, (e.Msg()));
|
LOG(LERROR, (e.Msg()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef openlr_obsolete
|
#ifdef openlr_obsolete
|
||||||
m_goldifyMatchedPathAction->setEnabled(true /* enabled */);
|
m_goldifyMatchedPathAction->setEnabled(true /* enabled */);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public:
|
|||||||
explicit MainWindow(Framework & framework);
|
explicit MainWindow(Framework & framework);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateTrafficPanel(std::string const & dataFilePath);
|
void CreateTrafficPanel();
|
||||||
void DestroyTrafficPanel();
|
void DestroyTrafficPanel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +57,7 @@ private:
|
|||||||
Framework & m_framework;
|
Framework & m_framework;
|
||||||
|
|
||||||
traffxml::TrafficModel * m_trafficModel = nullptr;
|
traffxml::TrafficModel * m_trafficModel = nullptr;
|
||||||
QDockWidget * m_docWidget = nullptr;
|
QDockWidget * m_dockWidget = nullptr;
|
||||||
|
|
||||||
#ifdef openlr_obsolete
|
#ifdef openlr_obsolete
|
||||||
QAction * m_goldifyMatchedPathAction = nullptr;
|
QAction * m_goldifyMatchedPathAction = nullptr;
|
||||||
|
|||||||
@@ -99,94 +99,223 @@ void RoadPointCandidate::SetActivePoint(FeatureID const & fid)
|
|||||||
} // namespace impl
|
} // namespace impl
|
||||||
#endif
|
#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)
|
||||||
|
// origin–destination 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::TrafficModel(std::string const & dataFileName, DataSource const & dataSource,
|
TrafficModel::TrafficModel(Framework & framework, DataSource const & dataSource,
|
||||||
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate,
|
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate, // TODO do we need that?
|
||||||
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate,
|
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate, // TODO do we need that?
|
||||||
QObject * parent)
|
QObject * parent)
|
||||||
: QAbstractTableModel(parent)
|
: QAbstractTableModel(parent)
|
||||||
, m_dataSource(dataSource)
|
, m_dataSource(dataSource)
|
||||||
, m_drawerDelegate(std::move(drawerDelegate))
|
, m_drawerDelegate(std::move(drawerDelegate))
|
||||||
, m_pointsDelegate(std::move(pointsDelegate))
|
, m_pointsDelegate(std::move(pointsDelegate))
|
||||||
{
|
{
|
||||||
// TODO(mgsergio): Collect stat how many segments of each kind were parsed.
|
framework.GetTrafficManager().SetTrafficUpdateCallbackFn([this, &framework](bool final) {
|
||||||
pugi::xml_document doc;
|
/*
|
||||||
if (!doc.load_file(dataFileName.data()))
|
* If final is true, this indicates the queue has been emptied and no further updates are
|
||||||
MYTHROW(TrafficModelError, ("Can't load file:", strerror(errno)));
|
* imminent. Such updates should always be processed. If final is false, we can optimize by
|
||||||
|
* selectively skipping updates.
|
||||||
// Save root node without children.
|
*/
|
||||||
{
|
GetPlatform().RunTask(Platform::Thread::Gui, [this, &framework]()
|
||||||
auto const root = doc.document_element();
|
|
||||||
auto node = m_template.append_child(root.name());
|
|
||||||
for (auto const & attr : root.attributes())
|
|
||||||
node.append_copy(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select all Segment elements that are direct children of the root.
|
|
||||||
auto const segments = doc.document_element().select_nodes("./Segment");
|
|
||||||
|
|
||||||
#ifdef openlr_obsolete
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto const & xpathNode : segments)
|
|
||||||
{
|
{
|
||||||
auto const xmlSegment = xpathNode.node();
|
beginResetModel();
|
||||||
|
auto const messageCache = framework.GetTrafficManager().GetMessageCache();
|
||||||
|
m_messages.clear();
|
||||||
|
m_messages.reserve(messageCache.size());
|
||||||
|
|
||||||
openlr::Path matchedPath;
|
for (auto & entry : messageCache)
|
||||||
openlr::Path fakePath;
|
m_messages.push_back(std::move(entry.second));
|
||||||
openlr::Path goldenPath;
|
|
||||||
|
|
||||||
openlr::LinearSegment segment;
|
endResetModel();
|
||||||
|
|
||||||
// TODO(mgsergio): Unify error handling interface of openlr_xml_mode and decoded_path parsers.
|
LOG(LINFO, ("Messages:", m_messages.size()));
|
||||||
auto const partnerSegmentXML = xmlSegment.child("reportSegments");
|
});
|
||||||
if (!openlr::SegmentFromXML(partnerSegmentXML, segment))
|
});
|
||||||
MYTHROW(TrafficModelError, ("An error occurred while parsing: can't parse segment"));
|
|
||||||
|
|
||||||
if (auto const route = xmlSegment.child("Route"))
|
|
||||||
openlr::PathFromXML(route, m_dataSource, matchedPath);
|
|
||||||
if (auto const route = xmlSegment.child("FakeRoute"))
|
|
||||||
openlr::PathFromXML(route, m_dataSource, fakePath);
|
|
||||||
if (auto const route = xmlSegment.child("GoldenRoute"))
|
|
||||||
openlr::PathFromXML(route, m_dataSource, goldenPath);
|
|
||||||
|
|
||||||
uint32_t positiveOffsetM = 0;
|
|
||||||
uint32_t negativeOffsetM = 0;
|
|
||||||
if (auto const reportSegmentLRC = partnerSegmentXML.child("ReportSegmentLRC"))
|
|
||||||
{
|
|
||||||
if (auto const method = reportSegmentLRC.child("method"))
|
|
||||||
{
|
|
||||||
if (auto const locationReference = method.child("olr:locationReference"))
|
|
||||||
{
|
|
||||||
if (auto const optionLinearLocationReference = locationReference
|
|
||||||
.child("olr:optionLinearLocationReference"))
|
|
||||||
{
|
|
||||||
if (auto const positiveOffset = optionLinearLocationReference.child("olr:positiveOffset"))
|
|
||||||
positiveOffsetM = UintValueFromXML(positiveOffset);
|
|
||||||
|
|
||||||
if (auto const negativeOffset = optionLinearLocationReference.child("olr:negativeOffset"))
|
|
||||||
negativeOffsetM = UintValueFromXML(negativeOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_segments.emplace_back(segment, positiveOffsetM, negativeOffsetM, matchedPath, fakePath,
|
|
||||||
goldenPath, partnerSegmentXML);
|
|
||||||
if (auto const status = xmlSegment.child("Ignored"))
|
|
||||||
{
|
|
||||||
if (status.text().as_bool())
|
|
||||||
m_segments.back().Ignore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (openlr::DecodedPathLoadError const & e)
|
|
||||||
{
|
|
||||||
MYTHROW(TrafficModelError, ("An exception occurred while parsing", dataFileName, e.Msg()));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
LOG(LINFO, (segments.size(), "segments are loaded."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mgsergio): Check if a path was committed, or commit it.
|
// TODO(mgsergio): Check if a path was committed, or commit it.
|
||||||
@@ -232,15 +361,10 @@ bool TrafficModel::SaveSampleAs(std::string const & fileName) const
|
|||||||
|
|
||||||
int TrafficModel::rowCount(const QModelIndex & parent) const
|
int TrafficModel::rowCount(const QModelIndex & parent) const
|
||||||
{
|
{
|
||||||
#ifdef openlr_obsolete
|
return static_cast<int>(m_messages.size());
|
||||||
return static_cast<int>(m_segments.size());
|
|
||||||
#else
|
|
||||||
// TODO return a meaningful value
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TrafficModel::columnCount(const QModelIndex & parent) const { return 4; }
|
int TrafficModel::columnCount(const QModelIndex & parent) const { return 2; }
|
||||||
|
|
||||||
QVariant TrafficModel::data(const QModelIndex & index, int role) const
|
QVariant TrafficModel::data(const QModelIndex & index, int role) const
|
||||||
{
|
{
|
||||||
@@ -266,24 +390,39 @@ QVariant TrafficModel::data(const QModelIndex & index, int role) const
|
|||||||
if (index.column() == 3)
|
if (index.column() == 3)
|
||||||
return m_segments[index.row()].GetNegativeOffset();
|
return m_segments[index.row()].GetNegativeOffset();
|
||||||
#endif
|
#endif
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return GetCountryAndRoadRef(m_messages[index.row()]);
|
||||||
|
case 1:
|
||||||
|
return GetDescription(m_messages[index.row()]);
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
return QVariant();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant TrafficModel::headerData(int section, Qt::Orientation orientation,
|
QVariant TrafficModel::headerData(int section, Qt::Orientation orientation,
|
||||||
int role /* = Qt::DisplayRole */) const
|
int role /* = Qt::DisplayRole */) const
|
||||||
{
|
{
|
||||||
if (orientation != Qt::Horizontal && role != Qt::DisplayRole)
|
/*
|
||||||
|
* 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();
|
return QVariant();
|
||||||
|
|
||||||
switch (section)
|
switch (section)
|
||||||
{
|
{
|
||||||
case 0: return "Segment id"; break;
|
case 0: return "Road ref"; break;
|
||||||
case 1: return "Status code"; break;
|
case 1: return "Description"; break;
|
||||||
case 2: return "Positive offset (Meters)"; break;
|
|
||||||
case 3: return "Negative offset (Meters)"; break;
|
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrafficModel::OnItemSelected(QItemSelection const & selected, QItemSelection const &)
|
void TrafficModel::OnItemSelected(QItemSelection const & selected, QItemSelection const &)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#ifdef openlr_obsolete
|
#ifdef openlr_obsolete
|
||||||
#include "openlr/decoded_path.hpp"
|
#include "openlr/decoded_path.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#include "traffxml/traff_model.hpp"
|
||||||
|
|
||||||
#include "indexer/data_source.hpp"
|
#include "indexer/data_source.hpp"
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ class TrafficModel : public QAbstractTableModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// TODO(mgsergio): Check we are on the right mwm. I.e. right mwm version and everything.
|
// TODO(mgsergio): Check we are on the right mwm. I.e. right mwm version and everything.
|
||||||
TrafficModel(std::string const & dataFileName, DataSource const & dataSource,
|
TrafficModel(Framework & framework, DataSource const & dataSource,
|
||||||
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate,
|
std::unique_ptr<TrafficDrawerDelegateBase> drawerDelegate,
|
||||||
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate,
|
std::unique_ptr<PointsControllerDelegateBase> pointsDelegate,
|
||||||
QObject * parent = Q_NULLPTR);
|
QObject * parent = Q_NULLPTR);
|
||||||
@@ -126,6 +127,12 @@ private:
|
|||||||
// Non-owning pointer to an element of m_segments.
|
// Non-owning pointer to an element of m_segments.
|
||||||
SegmentCorrespondence * m_currentSegment = nullptr;
|
SegmentCorrespondence * m_currentSegment = nullptr;
|
||||||
#endif
|
#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<TrafficDrawerDelegateBase> m_drawerDelegate;
|
||||||
std::unique_ptr<PointsControllerDelegateBase> m_pointsDelegate;
|
std::unique_ptr<PointsControllerDelegateBase> m_pointsDelegate;
|
||||||
|
|||||||
@@ -67,15 +67,23 @@ void TrafficPanel::CreateTable(QAbstractItemModel * trafficModel)
|
|||||||
m_table->setShowGrid(false);
|
m_table->setShowGrid(false);
|
||||||
m_table->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
|
m_table->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
|
||||||
m_table->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
|
m_table->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
|
||||||
m_table->verticalHeader()->setVisible(false);
|
|
||||||
m_table->horizontalHeader()->setVisible(true);
|
|
||||||
m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
||||||
|
|
||||||
m_table->setModel(trafficModel);
|
m_table->setModel(trafficModel);
|
||||||
m_table->setItemDelegate(new ComboBoxDelegate());
|
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(),
|
connect(m_table->selectionModel(),
|
||||||
SIGNAL(selectionChanged(QItemSelection const &, QItemSelection const &)),
|
SIGNAL(selectionChanged(QItemSelection const &, QItemSelection const &)),
|
||||||
trafficModel, SLOT(OnItemSelected(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
|
} // namespace traffxml
|
||||||
|
|||||||
Reference in New Issue
Block a user