From ad00c6da7fefcf0fce5ac4ed0f272b379b5da06f Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Jul 2021 12:41:43 +0200 Subject: [PATCH 1/9] LineGraphScene: load job stops LineGraphScene: remove friendship to LineGraphView, not needed anymore BackgroundHelper: draw visible job stops. --- src/graph/model/linegraphscene.cpp | 117 +++++++++++++++++++++++++-- src/graph/model/linegraphscene.h | 5 +- src/graph/model/stationgraphobject.h | 10 +++ src/graph/view/backgroundhelper.cpp | 42 ++++++++++ src/graph/view/backgroundhelper.h | 2 + src/graph/view/linegraphview.cpp | 1 + 6 files changed, 170 insertions(+), 7 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 1215968d..5c19cba9 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -6,6 +6,15 @@ #include +//TODO: maybe move to utils? +constexpr qreal MSEC_PER_HOUR = 1000 * 60 * 60; + +static inline qreal timeToHourFraction(const QTime &t) +{ + qreal ret = t.msecsSinceStartOfDay() / MSEC_PER_HOUR; + return ret; +} + LineGraphScene::LineGraphScene(sqlite3pp::database &db, QObject *parent) : QObject(parent), mDb(db), @@ -112,13 +121,13 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) } auto r = q.getRows(); -// TODO useful? -// outFromGateId = r.get(0); -// outToGateId = r.get(1); + // TODO useful? + // outFromGateId = r.get(0); + // outToGateId = r.get(1); graphObjectName = r.get(2); -// outSpeed = r.get(3); -// outType = utils::RailwaySegmentType(r.get(4)); -// outDistance = r.get(5); + // outSpeed = r.get(3); + // outType = utils::RailwaySegmentType(r.get(4)); + // outDistance = r.get(5); stA.stationId = r.get(6); stB.stationId = r.get(7); @@ -150,12 +159,31 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) recalcContentSize(); + reloadJobs(); + emit graphChanged(int(graphType), graphObjectId); emit redrawGraph(); return true; } +bool LineGraphScene::reloadJobs() +{ + if(graphType == LineGraphType::NoGraph) + return false; + + //TODO: maybe only load visible + //FIXME: also load job graph lines between stations + + for(StationGraphObject& st : stations) + { + if(!loadStationJobStops(st)) + return false; + } + + return true; +} + bool LineGraphScene::loadStation(StationGraphObject& st) { sqlite3pp::query q(mDb); @@ -287,3 +315,80 @@ bool LineGraphScene::loadFullLine(db_id lineId) return true; } + +bool LineGraphScene::loadStationJobStops(StationGraphObject &st) +{ + //Reset previous job graphs + for(StationGraphObject::PlatformGraph& platf : st.platforms) + { + platf.jobStops.clear(); + } + + sqlite3pp::query q(mDb, "SELECT stops.id, stops.job_id, jobs.category," + "stops.arrival, stops.departure," + "g_in.track_id, g_out.track_id" + " FROM stops" + " JOIN jobs ON stops.job_id=jobs.id" + " JOIN station_gate_connections g_in ON g_in.id=stops.in_gate_conn" + " JOIN station_gate_connections g_out ON g_out.id=stops.out_gate_conn" + " WHERE stops.station_id=?" + " ORDER BY stops.arrival"); + q.bind(1, st.stationId); + + const double vertOffset = Session->vertOffset; + const double hourOffset = Session->hourOffset; + + for(auto stop : q) + { + StationGraphObject::JobGraph jobStop; + jobStop.stopId = stop.get(0); + jobStop.jobId = stop.get(1); + jobStop.category = JobCategory(stop.get(2)); + QTime arrival = stop.get(3); + QTime departure = stop.get(4); + db_id trackId = stop.get(5); + db_id outTrackId = stop.get(6); + + if(trackId && trackId != outTrackId) + { + qWarning() << "Stop:" << jobStop.stopId << "Track not corresponding, using in"; + } + else if(!trackId) + { + if(outTrackId) + trackId = outTrackId; //First stop, use out gate connection + else + { + qWarning() << "Stop:" << jobStop.stopId << "Both in/out track NULL, skipping"; + continue; //Skip this stop + } + } + + StationGraphObject::PlatformGraph *platf = nullptr; + + //Find platform + for(StationGraphObject::PlatformGraph& p : st.platforms) + { + if(p.platformId == trackId) + { + platf = &p; + break; + } + } + + if(!platf) + { + //Requested platform is not in this station + qWarning() << "Stop:" << jobStop.stopId << "Track is not in this station"; + continue; //Skip this stop + } + + //Calculate coordinates + jobStop.arrivalY = vertOffset + timeToHourFraction(arrival) * hourOffset; + jobStop.departureY = vertOffset + timeToHourFraction(departure) * hourOffset; + + platf->jobStops.append(jobStop); + } + + return true; +} diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h index 3af1a073..9c073ce6 100644 --- a/src/graph/model/linegraphscene.h +++ b/src/graph/model/linegraphscene.h @@ -30,6 +30,8 @@ class LineGraphScene : public QObject bool loadGraph(db_id objectId, LineGraphType type, bool force = false); + bool reloadJobs(); + inline LineGraphType getGraphType() const { return graphType; @@ -62,8 +64,9 @@ public slots: bool loadStation(StationGraphObject &st); bool loadFullLine(db_id lineId); + bool loadStationJobStops(StationGraphObject &st); + private: - friend class LineGraphView; friend class BackgroundHelper; friend class LineGraphManager; diff --git a/src/graph/model/stationgraphobject.h b/src/graph/model/stationgraphobject.h index 70d178b4..4d74e903 100644 --- a/src/graph/model/stationgraphobject.h +++ b/src/graph/model/stationgraphobject.h @@ -17,12 +17,22 @@ class StationGraphObject QString stationName; utils::StationType stationType; + typedef struct + { + db_id jobId; + db_id stopId; + JobCategory category; + double arrivalY; + double departureY; + } JobGraph; + typedef struct { db_id platformId; QString platformName; QRgb color; QFlags platformType; + QVector jobStops; } PlatformGraph; QVector platforms; diff --git a/src/graph/view/backgroundhelper.cpp b/src/graph/view/backgroundhelper.cpp index 1eaaa5a4..74f34b93 100644 --- a/src/graph/view/backgroundhelper.cpp +++ b/src/graph/view/backgroundhelper.cpp @@ -195,3 +195,45 @@ void BackgroundHelper::drawStations(QPainter *painter, LineGraphScene *scene, co } } } + +void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF &rect) +{ + const double platfOffset = Session->platformOffset; + + QPen jobPen; + jobPen.setWidth(AppSettings.getJobLineWidth()); + + QPointF top; + QPointF bottom; + + for(const StationGraphObject &st : qAsConst(scene->stations)) + { + const double left = st.xPos; + const double right = left + st.platforms.count() * platfOffset; + + if(left > rect.right() || right < rect.left()) + continue; //Skip station, it's not visible + + top.rx() = bottom.rx() = st.xPos; + + for(const StationGraphObject::PlatformGraph& platf : st.platforms) + { + for(const StationGraphObject::JobGraph& jobStop : platf.jobStops) + { + if(jobStop.arrivalY > rect.bottom() || jobStop.departureY < rect.top()) + continue; //Skip, job not visible + + jobPen.setColor(Session->colorForCat(jobStop.category)); + painter->setPen(jobPen); + + top.setY(jobStop.arrivalY); + bottom.setY(jobStop.departureY); + + painter->drawLine(top, bottom); + } + + top.rx() += platfOffset; + bottom.rx() += platfOffset; + } + } +} diff --git a/src/graph/view/backgroundhelper.h b/src/graph/view/backgroundhelper.h index 0ce6acd6..1357be1e 100644 --- a/src/graph/view/backgroundhelper.h +++ b/src/graph/view/backgroundhelper.h @@ -17,6 +17,8 @@ class BackgroundHelper static void drawStationHeader(QPainter *painter, LineGraphScene *scene, const QRectF& rect, int horizontalScroll); static void drawStations(QPainter *painter, LineGraphScene *scene, const QRectF& rect); + + static void drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF& rect); }; #endif // BACKGROUNDHELPER_H diff --git a/src/graph/view/linegraphview.cpp b/src/graph/view/linegraphview.cpp index 90590619..036fefe4 100644 --- a/src/graph/view/linegraphview.cpp +++ b/src/graph/view/linegraphview.cpp @@ -100,6 +100,7 @@ void LineGraphView::paintEvent(QPaintEvent *e) return; //Nothing to draw BackgroundHelper::drawStations(&painter, m_scene, exposedRect); + BackgroundHelper::drawJobStops(&painter, m_scene, exposedRect); } void LineGraphView::resizeEvent(QResizeEvent *) From 404796d02ef13062c2ffbe43a64b5b9a5bf2ddce Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Jul 2021 13:02:19 +0200 Subject: [PATCH 2/9] LineGraphScene: fix job stop query stops.in_gate_conn might be NULL for first job stop stops.out_gate_conn might be NULL for last job stop Use LEFT JOIN to prevent excluding the stop. --- src/graph/model/linegraphscene.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 5c19cba9..a76c892d 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -329,8 +329,8 @@ bool LineGraphScene::loadStationJobStops(StationGraphObject &st) "g_in.track_id, g_out.track_id" " FROM stops" " JOIN jobs ON stops.job_id=jobs.id" - " JOIN station_gate_connections g_in ON g_in.id=stops.in_gate_conn" - " JOIN station_gate_connections g_out ON g_out.id=stops.out_gate_conn" + " LEFT JOIN station_gate_connections g_in ON g_in.id=stops.in_gate_conn" + " LEFT JOIN station_gate_connections g_out ON g_out.id=stops.out_gate_conn" " WHERE stops.station_id=?" " ORDER BY stops.arrival"); q.bind(1, st.stationId); From e8c49130cf3e6ee723beae9364c5ab9acaeb76a3 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Jul 2021 13:36:35 +0200 Subject: [PATCH 3/9] LineGraphScene: support tooltips on jobs --- src/graph/model/linegraphscene.cpp | 120 +++++++++++++++++++++++++++++ src/graph/model/linegraphscene.h | 4 + src/graph/view/linegraphview.cpp | 41 ++++++++++ src/graph/view/linegraphview.h | 1 + 4 files changed, 166 insertions(+) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index a76c892d..41c262e7 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -184,6 +184,126 @@ bool LineGraphScene::reloadJobs() return true; } +JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) +{ + const double platformOffset = Session->platformOffset; + + JobEntry job; + job.jobId = 0; + job.category = JobCategory::FREIGHT; + + if(stationPositions.isEmpty()) + return job; + + db_id prevStId = 0; + db_id nextStId = 0; + + for(const StationPosEntry& stPos : stationPositions) + { + if(stPos.xPos <= pos.x()) + prevStId = stPos.stationId; + + if(stPos.xPos >= pos.x()) + { + //We went past the requested position + nextStId = stPos.stationId; + break; + } + } + + auto prevSt = stations.constFind(prevStId); + if(prevSt == stations.constEnd()) + return job; //Error + + auto nextSt = stations.constFind(nextStId); + if(nextSt == stations.constEnd()) + return job; //Error + + const StationGraphObject::PlatformGraph *prevPlatf = nullptr; + const StationGraphObject::PlatformGraph *nextPlatf = nullptr; + double prevPos = 0; + double nextPos = 0; + + double xPos = prevSt->xPos; + for(const StationGraphObject::PlatformGraph& platf : prevSt->platforms) + { + if(xPos <= pos.x()) + { + prevPlatf = &platf; + prevPos = xPos; + } + if(xPos >= pos.x()) + { + //We went past the requested position + nextPlatf = &platf; + nextPos = xPos; + break; + } + + xPos += platformOffset; + } + + const double prevDistance = qAbs(prevPos - pos.x()); + if(prevPlatf && prevDistance > tolerance) + { + //Discard because too distant + prevPlatf = nullptr; + } + + if(!nextPlatf) + { + //Use second station + xPos = nextSt->xPos; + for(const StationGraphObject::PlatformGraph& platf : nextSt->platforms) + { + if(xPos <= pos.x()) + { + prevPlatf = &platf; + prevPos = xPos; + break; + } + + xPos += platformOffset; + } + } + + const double nextDistance = qAbs(nextPos - pos.x()); + if(nextPlatf && nextDistance > tolerance) + { + //Discard because too distant + nextPlatf = nullptr; + } + + const StationGraphObject::PlatformGraph *resultPlatf = nullptr; + if(!prevPlatf) + resultPlatf = nextPlatf; + else if(!nextPlatf) + resultPlatf = prevPlatf; + else + { + if(prevDistance < nextDistance) + resultPlatf = prevPlatf; + else + resultPlatf = nextPlatf; + } + + if(!resultPlatf) + return job; //No match + + for(const StationGraphObject::JobGraph& jobStop : resultPlatf->jobStops) + { + if(jobStop.arrivalY <= pos.y() && jobStop.departureY >= pos.y()) + { + //Found match + job.jobId = jobStop.jobId; + job.category = jobStop.category; + break; + } + } + + return job; +} + bool LineGraphScene::loadStation(StationGraphObject& st) { sqlite3pp::query q(mDb); diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h index 9c073ce6..1c760940 100644 --- a/src/graph/model/linegraphscene.h +++ b/src/graph/model/linegraphscene.h @@ -4,6 +4,8 @@ #include #include #include + +#include #include #include "utils/types.h" @@ -32,6 +34,8 @@ class LineGraphScene : public QObject bool reloadJobs(); + JobEntry getJobAt(const QPointF& pos, const double tolerance); + inline LineGraphType getGraphType() const { return graphType; diff --git a/src/graph/view/linegraphview.cpp b/src/graph/view/linegraphview.cpp index 036fefe4..3ca85888 100644 --- a/src/graph/view/linegraphview.cpp +++ b/src/graph/view/linegraphview.cpp @@ -6,6 +6,8 @@ #include "stationlabelsheader.h" #include "hourpanel.h" +#include "utils/jobcategorystrings.h" + #include "app/session.h" #include @@ -13,6 +15,9 @@ #include +#include +#include + LineGraphView::LineGraphView(QWidget *parent) : QAbstractScrollArea(parent), m_scene(nullptr) @@ -75,6 +80,42 @@ bool LineGraphView::event(QEvent *e) return QAbstractScrollArea::event(e); } +bool LineGraphView::viewportEvent(QEvent *e) +{ + if(e->type() == QEvent::ToolTip) + { + QHelpEvent *ev = static_cast(e); + + const QPoint origin(-horizontalScrollBar()->value(), -verticalScrollBar()->value()); + + QPoint pos = ev->pos(); + const QRect vp = viewport()->rect(); + + //Map to viewport + pos -= vp.topLeft(); + if(pos.x() < 0 || pos.y() < 0) + return false; + + //Map to scene + pos -= origin; + + JobEntry job = m_scene->getJobAt(pos, Session->platformOffset / 2); + + if(job.jobId) + { + QToolTip::showText(ev->globalPos(), + JobCategoryName::jobName(job.jobId, job.category), + viewport()); + }else{ + QToolTip::hideText(); + } + + return true; + } + + return QAbstractScrollArea::viewportEvent(e); +} + void LineGraphView::paintEvent(QPaintEvent *e) { const QPoint origin(-horizontalScrollBar()->value(), -verticalScrollBar()->value()); diff --git a/src/graph/view/linegraphview.h b/src/graph/view/linegraphview.h index 381762bc..f80ddaf1 100644 --- a/src/graph/view/linegraphview.h +++ b/src/graph/view/linegraphview.h @@ -27,6 +27,7 @@ public slots: protected: bool event(QEvent *e) override; + bool viewportEvent(QEvent *e) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *) override; void mousePressEvent(QMouseEvent *e) override; From ac67f4f40fa8c68fe56e879991e5b4bf38f9d5f7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Tue, 20 Jul 2021 15:10:18 +0200 Subject: [PATCH 4/9] Initial job segment graph --- src/graph/model/linegraphscene.cpp | 70 ++++++++++++++++++++++++++---- src/graph/model/linegraphscene.h | 16 ++++--- 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 41c262e7..43477d75 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -97,7 +97,7 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) //Register a single station at start position st.xPos = curPos; stations.insert(st.stationId, st); - stationPositions = {{st.stationId, st.xPos}}; + stationPositions = {{st.stationId, 0, st.xPos}}; graphObjectName = st.stationName; } else if(type == LineGraphType::RailwaySegment) @@ -141,8 +141,8 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) stations.insert(stA.stationId, stA); stations.insert(stB.stationId, stB); - stationPositions = {{stA.stationId, stA.xPos}, - {stB.stationId, stB.xPos}}; + stationPositions = {{stA.stationId, objectId, stA.xPos}, + {stB.stationId, 0, stB.xPos}}; } else if(type == LineGraphType::RailwayLine) { @@ -181,6 +181,31 @@ bool LineGraphScene::reloadJobs() return false; } + for(int i = 0; i < stationPositions.size(); i++) + { + StationPosEntry& stPos = stationPositions[i]; + if(!stPos.segmentId) + continue; //No segment, skip + + db_id fromStId = stPos.stationId; + db_id toStId = 0; + if(i <= stationPositions.size() - 1) + toStId = stationPositions.at(i + 1).stationId; + + if(!toStId) + break; //No next station + + auto fromSt = stations.constFind(fromStId); + if(fromSt == stations.constEnd()) + continue; + + auto toSt = stations.constFind(toStId); + if(toSt == stations.constEnd()) + continue; + + + } + return true; } @@ -258,8 +283,8 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) { if(xPos <= pos.x()) { - prevPlatf = &platf; - prevPos = xPos; + nextPlatf = &platf; + nextPos = xPos; break; } @@ -380,7 +405,7 @@ bool LineGraphScene::loadFullLine(db_id lineId) for(auto seg : q) { db_id lineSegmentId = seg.get(0); - //item.railwaySegmentId = seg.get(1); + db_id railwaySegmentId = seg.get(1); bool reversed = seg.get(2) != 0; //item.segmentName = seg.get(3); @@ -410,7 +435,7 @@ bool LineGraphScene::loadFullLine(db_id lineId) st.xPos = curPos; stations.insert(st.stationId, st); - stationPositions.append({st.stationId, st.xPos}); + stationPositions.append({st.stationId, railwaySegmentId, st.xPos}); curPos += st.platforms.count() * Session->platformOffset + Session->stationOffset; } else if(fromStationId != lastStationId) @@ -427,7 +452,9 @@ bool LineGraphScene::loadFullLine(db_id lineId) stB.xPos = curPos; stations.insert(stB.stationId, stB); - stationPositions.append({stB.stationId, stB.xPos}); + + stationPositions.last().segmentId = railwaySegmentId; + stationPositions.append({stB.stationId, 0, stB.xPos}); curPos += stB.platforms.count() * Session->platformOffset + Session->stationOffset; lastStationId = stB.stationId; @@ -512,3 +539,30 @@ bool LineGraphScene::loadStationJobStops(StationGraphObject &st) return true; } + +bool LineGraphScene::loadSegmentJobs(LineGraphScene::StationPosEntry& stPos, const StationGraphObject& fromStm, const StationGraphObject& toSt) +{ + sqlite3pp::query q(mDb, "SELECT sub.*, jobs.category, g_out.track_id, g_in.track_id FROM (" + " SELECT stops.id AS cur_stop_id, lead(stops.id, 1) OVER win AS next_stop_id, stops.station_id," + " stops.job_id," + " stops.departure, lead(stops.arrival, 1) OVER win AS next_stop_arrival," + " stops.out_gate_conn," + " lead(stops.in_gate_conn, 1) OVER win AS next_stop_g_in," + " seg_conn.seg_id" + " FROM stops" + " JOIN railway_connections seg_conn ON seg_conn.id=stops.next_segment_conn_id" + " WINDOW win AS (PARTITION BY stops.job_id ORDER BY stops.arrival)" + ") AS sub" + " JOIN station_gate_connections g_out ON g_out.id=sub.out_gate_conn" + " JOIN station_gate_connections g_in ON g_in.id=sub.next_stop_g_in" + " JOIN jobs ON jobs.id=sub.job_id" + " WHERE sub.seg_id=?"); + + q.bind(1, stPos.segmentId); + for(auto stop : q) + { + //TODO: store line in stPos + } + + return true; +} diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h index 1c760940..b19ee75a 100644 --- a/src/graph/model/linegraphscene.h +++ b/src/graph/model/linegraphscene.h @@ -63,12 +63,22 @@ class LineGraphScene : public QObject public slots: void reload(); +private: + + typedef struct + { + db_id stationId; + db_id segmentId; + double xPos; + } StationPosEntry; + private: void recalcContentSize(); bool loadStation(StationGraphObject &st); bool loadFullLine(db_id lineId); bool loadStationJobStops(StationGraphObject &st); + bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromStm, const StationGraphObject &toSt); private: friend class BackgroundHelper; @@ -82,12 +92,6 @@ public slots: QString graphObjectName; - typedef struct - { - db_id stationId; - double xPos; - } StationPosEntry; - QVector stationPositions; QHash stations; From 04159bbca27973280f4ddb2bd668f90f3f4f670a Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 25 Aug 2021 17:04:46 +0200 Subject: [PATCH 5/9] Add struct JobLineGraph to draw moving job graph * Rename job stop graph to JobStopGraph * Document code --- src/graph/model/stationgraphobject.h | 41 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/graph/model/stationgraphobject.h b/src/graph/model/stationgraphobject.h index 4d74e903..6b1b26ab 100644 --- a/src/graph/model/stationgraphobject.h +++ b/src/graph/model/stationgraphobject.h @@ -8,6 +8,11 @@ #include "stations/station_utils.h" #include +/*! + * Graph of a railway station + * + * Contains informations to draw station name, platforms and jobs + */ class StationGraphObject { public: @@ -17,22 +22,52 @@ class StationGraphObject QString stationName; utils::StationType stationType; + /*! + * Graph of the job while is stopping + * + * Contains informations to draw job line on top of the PlatformGraph + */ typedef struct { db_id jobId; - db_id stopId; JobCategory category; + + db_id stopId; double arrivalY; double departureY; - } JobGraph; + } JobStopGraph; + + /*! + * Graph of the job while is moving + * + * Contains informations to draw job line between two adjacent stations + */ + typedef struct + { + db_id jobId; + JobCategory category; + + db_id fromStopId; + db_id fromPlatfId; + double fromDepartureY; + + db_id toStopId; + db_id toPlatfId; + double toArrivalY; + } JobLineGraph; + /*! + * Graph of a station track (platform) + * + * Contains informations to draw platform line and header name + */ typedef struct { db_id platformId; QString platformName; QRgb color; QFlags platformType; - QVector jobStops; + QVector jobStops; } PlatformGraph; QVector platforms; From 433216d666d540351c0639e6252029e8cd7f4443 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 25 Aug 2021 17:05:06 +0200 Subject: [PATCH 6/9] JobStopGraph: rename occurrencies --- src/graph/model/linegraphscene.cpp | 4 ++-- src/graph/view/backgroundhelper.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 43477d75..42693a0b 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -315,7 +315,7 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) if(!resultPlatf) return job; //No match - for(const StationGraphObject::JobGraph& jobStop : resultPlatf->jobStops) + for(const StationGraphObject::JobStopGraph& jobStop : resultPlatf->jobStops) { if(jobStop.arrivalY <= pos.y() && jobStop.departureY >= pos.y()) { @@ -487,7 +487,7 @@ bool LineGraphScene::loadStationJobStops(StationGraphObject &st) for(auto stop : q) { - StationGraphObject::JobGraph jobStop; + StationGraphObject::JobStopGraph jobStop; jobStop.stopId = stop.get(0); jobStop.jobId = stop.get(1); jobStop.category = JobCategory(stop.get(2)); diff --git a/src/graph/view/backgroundhelper.cpp b/src/graph/view/backgroundhelper.cpp index 74f34b93..9be8b0aa 100644 --- a/src/graph/view/backgroundhelper.cpp +++ b/src/graph/view/backgroundhelper.cpp @@ -218,7 +218,7 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co for(const StationGraphObject::PlatformGraph& platf : st.platforms) { - for(const StationGraphObject::JobGraph& jobStop : platf.jobStops) + for(const StationGraphObject::JobStopGraph& jobStop : platf.jobStops) { if(jobStop.arrivalY > rect.bottom() || jobStop.departureY < rect.top()) continue; //Skip, job not visible From 6e954c4585e2b25e5b6397c2d13d7b8469b419b7 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Wed, 25 Aug 2021 17:50:02 +0200 Subject: [PATCH 7/9] Add Doxygen comments to graph folder classes Document classes in the 'src/graph' directory --- src/graph/model/linegraphmanager.h | 9 +++++++++ src/graph/model/linegraphscene.h | 19 +++++++++++++++---- src/graph/model/stationgraphobject.h | 11 +++++++---- src/graph/view/backgroundhelper.h | 9 +++++++++ src/graph/view/hourpanel.h | 9 +++++++++ src/graph/view/linegraphtoolbar.h | 8 ++++++++ src/graph/view/linegraphview.h | 9 +++++++++ src/graph/view/linegraphwidget.h | 10 ++++++++++ src/graph/view/stationlabelsheader.h | 10 ++++++++++ 9 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/graph/model/linegraphmanager.h b/src/graph/model/linegraphmanager.h index 26b55041..5908bdfc 100644 --- a/src/graph/model/linegraphmanager.h +++ b/src/graph/model/linegraphmanager.h @@ -9,6 +9,15 @@ class LineGraphScene; +/*! + * \brief Class for managing LineGraphScene instances + * + * The manager listens for changes on railway plan and + * decides which of the registered LineGraphScene needs + * to be updated. + * + * \sa LineGraphScene + */ class LineGraphManager : public QObject { Q_OBJECT diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h index b19ee75a..a3807b2a 100644 --- a/src/graph/model/linegraphscene.h +++ b/src/graph/model/linegraphscene.h @@ -16,14 +16,25 @@ namespace sqlite3pp { class database; } +/*! + * \brief Enum to describe view content type + */ enum class LineGraphType { - NoGraph = 0, - SingleStation, - RailwaySegment, - RailwayLine + NoGraph = 0, //!< No content displayed + SingleStation, //!< Show a single station + RailwaySegment, //!< Show two adjacent stations and the segment in between + RailwayLine //!< Show a complete railway line (multiple adjacent segments) }; +/*! + * \brief Class to store line information + * + * Stores information to draw railway content in a LineGraphView + * + * \sa LineGraphManager + * \sa LineGraphView + */ class LineGraphScene : public QObject { Q_OBJECT diff --git a/src/graph/model/stationgraphobject.h b/src/graph/model/stationgraphobject.h index 6b1b26ab..97cdd193 100644 --- a/src/graph/model/stationgraphobject.h +++ b/src/graph/model/stationgraphobject.h @@ -9,9 +9,10 @@ #include /*! - * Graph of a railway station + * \brief Graph of a railway station * * Contains informations to draw station name, platforms and jobs + * \sa PlatformGraph */ class StationGraphObject { @@ -23,7 +24,7 @@ class StationGraphObject utils::StationType stationType; /*! - * Graph of the job while is stopping + * \brief Graph of the job while is stopping * * Contains informations to draw job line on top of the PlatformGraph */ @@ -38,7 +39,7 @@ class StationGraphObject } JobStopGraph; /*! - * Graph of the job while is moving + * \brief Graph of the job while is moving * * Contains informations to draw job line between two adjacent stations */ @@ -57,9 +58,11 @@ class StationGraphObject } JobLineGraph; /*! - * Graph of a station track (platform) + * \brief Graph of a station track (platform) * * Contains informations to draw platform line and header name + * \sa JobStopGraph + * \sa JobLineGraph */ typedef struct { diff --git a/src/graph/view/backgroundhelper.h b/src/graph/view/backgroundhelper.h index 1357be1e..fad1686e 100644 --- a/src/graph/view/backgroundhelper.h +++ b/src/graph/view/backgroundhelper.h @@ -7,6 +7,15 @@ class QPainter; class LineGraphScene; +/*! + * \brief Helper class to render LineGraphView contents + * + * Contains static helper functions to draw each part of the view + * + * \sa LineGraphView + * \sa HourPanel + * \sa StationLabelsHeader + */ class BackgroundHelper { public: diff --git a/src/graph/view/hourpanel.h b/src/graph/view/hourpanel.h index 1534abd0..dba5c93a 100644 --- a/src/graph/view/hourpanel.h +++ b/src/graph/view/hourpanel.h @@ -3,6 +3,15 @@ #include +/*! + * \brief Helper widget to draw hour labels + * + * Draws hour labels on the view vertical header + * + * \sa LineGraphScene + * \sa LineGraphView + * \sa BackgroundHelper + */ class HourPanel : public QWidget { Q_OBJECT diff --git a/src/graph/view/linegraphtoolbar.h b/src/graph/view/linegraphtoolbar.h index d640c3d9..7f49d8cf 100644 --- a/src/graph/view/linegraphtoolbar.h +++ b/src/graph/view/linegraphtoolbar.h @@ -12,6 +12,14 @@ class CustomCompletionLineEdit; class LineGraphScene; class ISqlFKMatchModel; +/*! + * \brief Toolbar to select railway stations or lines + * + * Consist of a combobox and a line edit + * The combo box selects the content type (\sa LineGraphType) + * The line edit allows to choose which item of selected type + * should be shown. + */ class LineGraphToolbar : public QWidget { Q_OBJECT diff --git a/src/graph/view/linegraphview.h b/src/graph/view/linegraphview.h index f80ddaf1..c11d16ab 100644 --- a/src/graph/view/linegraphview.h +++ b/src/graph/view/linegraphview.h @@ -8,6 +8,15 @@ class LineGraphScene; class StationLabelsHeader; class HourPanel; +/*! + * \brief Widget to display a LineGraphScene + * + * A scrollable widget which renders LineGraphScene contents + * Moving the mouse cursor on the contents, tooltips will show + * + * \sa LineGraphWidget + * \sa BackgroundHelper + */ class LineGraphView : public QAbstractScrollArea { Q_OBJECT diff --git a/src/graph/view/linegraphwidget.h b/src/graph/view/linegraphwidget.h index 99f68c23..43c10cdf 100644 --- a/src/graph/view/linegraphwidget.h +++ b/src/graph/view/linegraphwidget.h @@ -9,6 +9,16 @@ class LineGraphScene; class LineGraphView; class LineGraphToolbar; +/*! + * \brief An all-in-one widget as a railway line view + * + * This widget encapsulate a toolbar to select which + * contents user wants to see (stations, segments or railway lines) + * and a view which renders the chosen contents + * + * \sa LineGraphToolbar + * \sa LineGraphView + */ class LineGraphWidget : public QWidget { Q_OBJECT diff --git a/src/graph/view/stationlabelsheader.h b/src/graph/view/stationlabelsheader.h index 0b3297a8..08608062 100644 --- a/src/graph/view/stationlabelsheader.h +++ b/src/graph/view/stationlabelsheader.h @@ -5,6 +5,16 @@ class LineGraphScene; +/*! + * \brief Helper widget to draw station labels + * + * Draws station labels (with platform numbers below) on + * the view horizontal header + * + * \sa LineGraphScene + * \sa LineGraphView + * \sa BackgroundHelper + */ class StationLabelsHeader : public QWidget { Q_OBJECT From 3b8d4f1cc6d88d2af24792f3dc1ae4f266578822 Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 26 Aug 2021 11:07:26 +0200 Subject: [PATCH 8/9] Load job segments and draw them JobLineGraph: moved from StationGraphObject to LineGraphScene JobLineGraph: renamed to JobSegmentGraph LineGraphScene: do not warn on last stop if tracks do not match LineGraphScene: fix job segment query --- src/graph/model/linegraphscene.cpp | 95 +++++++++++++++++++++++----- src/graph/model/linegraphscene.h | 32 +++++++++- src/graph/model/stationgraphobject.h | 21 +----- src/graph/view/backgroundhelper.cpp | 44 +++++++++++++ src/graph/view/backgroundhelper.h | 2 + src/graph/view/linegraphview.cpp | 1 + 6 files changed, 159 insertions(+), 36 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 42693a0b..47ac4e3e 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -15,6 +15,18 @@ static inline qreal timeToHourFraction(const QTime &t) return ret; } +static inline double stationPlatformPosition(const StationGraphObject& st, const db_id platfId, const double platfOffset) +{ + double x = st.xPos; + for(const StationGraphObject::PlatformGraph& platf : st.platforms) + { + if(platf.platformId == platfId) + return x; + x += platfOffset; + } + return -1; +} + LineGraphScene::LineGraphScene(sqlite3pp::database &db, QObject *parent) : QObject(parent), mDb(db), @@ -97,7 +109,7 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) //Register a single station at start position st.xPos = curPos; stations.insert(st.stationId, st); - stationPositions = {{st.stationId, 0, st.xPos}}; + stationPositions = {{st.stationId, 0, st.xPos, {}}}; graphObjectName = st.stationName; } else if(type == LineGraphType::RailwaySegment) @@ -141,8 +153,8 @@ bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) stations.insert(stA.stationId, stA); stations.insert(stB.stationId, stB); - stationPositions = {{stA.stationId, objectId, stA.xPos}, - {stB.stationId, 0, stB.xPos}}; + stationPositions = {{stA.stationId, objectId, stA.xPos, {}}, + {stB.stationId, 0, stB.xPos, {}}}; } else if(type == LineGraphType::RailwayLine) { @@ -173,7 +185,6 @@ bool LineGraphScene::reloadJobs() return false; //TODO: maybe only load visible - //FIXME: also load job graph lines between stations for(StationGraphObject& st : stations) { @@ -181,6 +192,9 @@ bool LineGraphScene::reloadJobs() return false; } + //Save last station from previous iteration + auto lastSt = stations.constEnd(); + for(int i = 0; i < stationPositions.size(); i++) { StationPosEntry& stPos = stationPositions[i]; @@ -195,15 +209,25 @@ bool LineGraphScene::reloadJobs() if(!toStId) break; //No next station - auto fromSt = stations.constFind(fromStId); - if(fromSt == stations.constEnd()) - continue; + auto fromSt = lastSt; + if(fromSt == stations.constEnd() || fromSt->stationId != fromStId) + { + fromSt = stations.constFind(fromStId); + if(fromSt == stations.constEnd()) + { + continue; + } + } auto toSt = stations.constFind(toStId); if(toSt == stations.constEnd()) continue; + if(!loadSegmentJobs(stPos, fromSt.value(), toSt.value())) + return false; + //Store last station + lastSt = toSt; } return true; @@ -223,7 +247,7 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) db_id prevStId = 0; db_id nextStId = 0; - for(const StationPosEntry& stPos : stationPositions) + for(const StationPosEntry& stPos : qAsConst(stationPositions)) { if(stPos.xPos <= pos.x()) prevStId = stPos.stationId; @@ -435,7 +459,7 @@ bool LineGraphScene::loadFullLine(db_id lineId) st.xPos = curPos; stations.insert(st.stationId, st); - stationPositions.append({st.stationId, railwaySegmentId, st.xPos}); + stationPositions.append({st.stationId, railwaySegmentId, st.xPos, {}}); curPos += st.platforms.count() * Session->platformOffset + Session->stationOffset; } else if(fromStationId != lastStationId) @@ -454,7 +478,7 @@ bool LineGraphScene::loadFullLine(db_id lineId) stations.insert(stB.stationId, stB); stationPositions.last().segmentId = railwaySegmentId; - stationPositions.append({stB.stationId, 0, stB.xPos}); + stationPositions.append({stB.stationId, 0, stB.xPos, {}}); curPos += stB.platforms.count() * Session->platformOffset + Session->stationOffset; lastStationId = stB.stationId; @@ -496,8 +520,9 @@ bool LineGraphScene::loadStationJobStops(StationGraphObject &st) db_id trackId = stop.get(5); db_id outTrackId = stop.get(6); - if(trackId && trackId != outTrackId) + if(trackId && outTrackId && trackId != outTrackId) { + //Not last stop, neither first stop. Tracks must correspond qWarning() << "Stop:" << jobStop.stopId << "Track not corresponding, using in"; } else if(!trackId) @@ -540,17 +565,25 @@ bool LineGraphScene::loadStationJobStops(StationGraphObject &st) return true; } -bool LineGraphScene::loadSegmentJobs(LineGraphScene::StationPosEntry& stPos, const StationGraphObject& fromStm, const StationGraphObject& toSt) +bool LineGraphScene::loadSegmentJobs(LineGraphScene::StationPosEntry& stPos, const StationGraphObject& fromSt, const StationGraphObject& toSt) { + //Reset previous job segment graph + stPos.nextSegmentJobGraphs.clear(); + + const double vertOffset = Session->vertOffset; + const double hourOffset = Session->hourOffset; + const double platfOffset = Session->platformOffset; + sqlite3pp::query q(mDb, "SELECT sub.*, jobs.category, g_out.track_id, g_in.track_id FROM (" - " SELECT stops.id AS cur_stop_id, lead(stops.id, 1) OVER win AS next_stop_id, stops.station_id," + " SELECT stops.id AS cur_stop_id, lead(stops.id, 1) OVER win AS next_stop_id," + " stops.station_id," " stops.job_id," " stops.departure, lead(stops.arrival, 1) OVER win AS next_stop_arrival," " stops.out_gate_conn," " lead(stops.in_gate_conn, 1) OVER win AS next_stop_g_in," " seg_conn.seg_id" " FROM stops" - " JOIN railway_connections seg_conn ON seg_conn.id=stops.next_segment_conn_id" + " LEFT JOIN railway_connections seg_conn ON seg_conn.id=stops.next_segment_conn_id" " WINDOW win AS (PARTITION BY stops.job_id ORDER BY stops.arrival)" ") AS sub" " JOIN station_gate_connections g_out ON g_out.id=sub.out_gate_conn" @@ -561,7 +594,39 @@ bool LineGraphScene::loadSegmentJobs(LineGraphScene::StationPosEntry& stPos, con q.bind(1, stPos.segmentId); for(auto stop : q) { - //TODO: store line in stPos + JobSegmentGraph job; + job.fromStopId = stop.get(0); + job.toStopId = stop.get(1); + db_id stId = stop.get(2); + job.jobId = stop.get(3); + QTime departure = stop.get(4); + QTime arrival = stop.get(5); + //6 - out gate connection + //7 - in gate connection + //8 - segment_id + job.category = JobCategory(stop.get(9)); + job.fromPlatfId = stop.get(10); + job.toPlatfId = stop.get(11); + + if(toSt.stationId == stId) + { + //Job goes in opposite direction, reverse stops + qSwap(job.fromStopId, job.toStopId); + qSwap(job.fromPlatfId, job.toPlatfId); + qSwap(departure, arrival); + } + + //Calculate coordinates + job.fromDeparture.rx() = stationPlatformPosition(fromSt, job.fromPlatfId, platfOffset); + job.fromDeparture.ry() = vertOffset + timeToHourFraction(departure) * hourOffset; + + job.toArrival.rx() = stationPlatformPosition(toSt, job.toPlatfId, platfOffset); + job.toArrival.ry() = vertOffset + timeToHourFraction(arrival) * hourOffset; + + if(job.fromDeparture.x() < 0 || job.toArrival.x() < 0) + continue; //Skip, couldn't find platform + + stPos.nextSegmentJobGraphs.append(job); } return true; diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h index a3807b2a..acc0f3e1 100644 --- a/src/graph/model/linegraphscene.h +++ b/src/graph/model/linegraphscene.h @@ -76,11 +76,41 @@ public slots: private: + /*! + * \brief Graph of the job while is moving + * + * Contains informations to draw job line between two adjacent stations + */ + typedef struct + { + db_id jobId; + JobCategory category; + + db_id fromStopId; + db_id fromPlatfId; + QPointF fromDeparture; + + db_id toStopId; + db_id toPlatfId; + QPointF toArrival; + } JobSegmentGraph; + + /*! + * \brief Station entry on scene + * + * Represents a station item placeholder in an ordered list of scene + */ typedef struct { db_id stationId; db_id segmentId; double xPos; + + QVector nextSegmentJobGraphs; + /*!< + * Stores job graph of the next segment + * Which means jobs departing from this staation and going to next one + */ } StationPosEntry; private: @@ -89,7 +119,7 @@ public slots: bool loadFullLine(db_id lineId); bool loadStationJobStops(StationGraphObject &st); - bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromStm, const StationGraphObject &toSt); + bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromSt, const StationGraphObject &toSt); private: friend class BackgroundHelper; diff --git a/src/graph/model/stationgraphobject.h b/src/graph/model/stationgraphobject.h index 97cdd193..c5354990 100644 --- a/src/graph/model/stationgraphobject.h +++ b/src/graph/model/stationgraphobject.h @@ -12,6 +12,7 @@ * \brief Graph of a railway station * * Contains informations to draw station name, platforms and jobs + * * \sa PlatformGraph */ class StationGraphObject @@ -38,31 +39,11 @@ class StationGraphObject double departureY; } JobStopGraph; - /*! - * \brief Graph of the job while is moving - * - * Contains informations to draw job line between two adjacent stations - */ - typedef struct - { - db_id jobId; - JobCategory category; - - db_id fromStopId; - db_id fromPlatfId; - double fromDepartureY; - - db_id toStopId; - db_id toPlatfId; - double toArrivalY; - } JobLineGraph; - /*! * \brief Graph of a station track (platform) * * Contains informations to draw platform line and header name * \sa JobStopGraph - * \sa JobLineGraph */ typedef struct { diff --git a/src/graph/view/backgroundhelper.cpp b/src/graph/view/backgroundhelper.cpp index 9be8b0aa..3b0430b8 100644 --- a/src/graph/view/backgroundhelper.cpp +++ b/src/graph/view/backgroundhelper.cpp @@ -220,6 +220,7 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co { for(const StationGraphObject::JobStopGraph& jobStop : platf.jobStops) { + //NOTE: departure comes AFTER arrival in time, opposite than job segment if(jobStop.arrivalY > rect.bottom() || jobStop.departureY < rect.top()) continue; //Skip, job not visible @@ -237,3 +238,46 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co } } } + +void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect) +{ + const double stationOffset = Session->stationOffset; + + QPen jobPen; + jobPen.setWidth(AppSettings.getJobLineWidth()); + + //Iterate until one but last + //This way we can always acces next station + for(int i = 0; i < scene->stationPositions.size() - 1; i++) + { + const LineGraphScene::StationPosEntry& stPos = scene->stationPositions.at(i); + + const double left = stPos.xPos; + double right = 0; + + if(i < scene->stationPositions.size() - 2) + { + const LineGraphScene::StationPosEntry& afterNextPos = scene->stationPositions.at(i + 2); + right = afterNextPos.xPos - stationOffset; + } + else + { + right = rect.right(); //Last station, use all space on right side + } + + if(left > rect.right() || right < rect.left()) + continue; //Skip station, it's not visible + + for(const LineGraphScene::JobSegmentGraph& job : stPos.nextSegmentJobGraphs) + { + //NOTE: departure comes BEFORE arrival in time, opposite than job stop + if(job.fromDeparture.y() > rect.bottom() || job.toArrival.y() < rect.top()) + continue; //Skip, job not visible + + jobPen.setColor(Session->colorForCat(job.category)); + painter->setPen(jobPen); + + painter->drawLine(job.fromDeparture, job.toArrival); + } + } +} diff --git a/src/graph/view/backgroundhelper.h b/src/graph/view/backgroundhelper.h index fad1686e..b9f043fb 100644 --- a/src/graph/view/backgroundhelper.h +++ b/src/graph/view/backgroundhelper.h @@ -28,6 +28,8 @@ class BackgroundHelper static void drawStations(QPainter *painter, LineGraphScene *scene, const QRectF& rect); static void drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF& rect); + + static void drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect); }; #endif // BACKGROUNDHELPER_H diff --git a/src/graph/view/linegraphview.cpp b/src/graph/view/linegraphview.cpp index 3ca85888..27e26e58 100644 --- a/src/graph/view/linegraphview.cpp +++ b/src/graph/view/linegraphview.cpp @@ -142,6 +142,7 @@ void LineGraphView::paintEvent(QPaintEvent *e) BackgroundHelper::drawStations(&painter, m_scene, exposedRect); BackgroundHelper::drawJobStops(&painter, m_scene, exposedRect); + BackgroundHelper::drawJobSegments(&painter, m_scene, exposedRect); } void LineGraphView::resizeEvent(QResizeEvent *) From 6918fb0b10bb58729ee70fdfa51b96538cf0253e Mon Sep 17 00:00:00 2001 From: Filippo Gentile Date: Thu, 26 Aug 2021 12:06:48 +0200 Subject: [PATCH 9/9] LineGraphScene: fix tooltip logic --- src/graph/model/linegraphscene.cpp | 42 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp index 47ac4e3e..6dfcb6ff 100644 --- a/src/graph/model/linegraphscene.cpp +++ b/src/graph/model/linegraphscene.cpp @@ -261,11 +261,9 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) } auto prevSt = stations.constFind(prevStId); - if(prevSt == stations.constEnd()) - return job; //Error - auto nextSt = stations.constFind(nextStId); - if(nextSt == stations.constEnd()) + + if(prevSt == stations.constEnd() && nextSt == stations.constEnd()) return job; //Error const StationGraphObject::PlatformGraph *prevPlatf = nullptr; @@ -273,23 +271,27 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) double prevPos = 0; double nextPos = 0; - double xPos = prevSt->xPos; - for(const StationGraphObject::PlatformGraph& platf : prevSt->platforms) + double xPos = 0; + if(prevSt != stations.constEnd()) { - if(xPos <= pos.x()) - { - prevPlatf = &platf; - prevPos = xPos; - } - if(xPos >= pos.x()) + xPos = prevSt->xPos; + for(const StationGraphObject::PlatformGraph& platf : prevSt->platforms) { - //We went past the requested position - nextPlatf = &platf; - nextPos = xPos; - break; - } + if(xPos <= pos.x()) + { + prevPlatf = &platf; + prevPos = xPos; + } + if(xPos >= pos.x()) + { + //We went past the requested position + nextPlatf = &platf; + nextPos = xPos; + break; + } - xPos += platformOffset; + xPos += platformOffset; + } } const double prevDistance = qAbs(prevPos - pos.x()); @@ -299,13 +301,13 @@ JobEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance) prevPlatf = nullptr; } - if(!nextPlatf) + if(!nextPlatf && nextSt != stations.constEnd()) { //Use second station xPos = nextSt->xPos; for(const StationGraphObject::PlatformGraph& platf : nextSt->platforms) { - if(xPos <= pos.x()) + if(xPos >= pos.x()) { nextPlatf = &platf; nextPos = xPos;