File indexing completed on 2024-05-12 04:42:52

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "vehiclelayoutquerymodel.h"
0008 #include "abstractquerymodel_p.h"
0009 
0010 #include <KPublicTransport/Attribution>
0011 #include <KPublicTransport/VehicleLayoutReply>
0012 #include <KPublicTransport/Manager>
0013 
0014 #include <QDebug>
0015 
0016 using namespace KPublicTransport;
0017 
0018 namespace KPublicTransport {
0019 class VehicleLayoutQueryModelPrivate : public AbstractQueryModelPrivate
0020 {
0021 public:
0022     void doQuery() override;
0023     void doClearResults() override;
0024 
0025     void interpolatePlatformPositionsFromSectionName();
0026     template <typename Iter>
0027     void interpolatePlatformPositionsFromSectionName(Iter begin, Iter end);
0028 
0029     VehicleLayoutRequest m_request;
0030     Stopover m_stopover;
0031 
0032     Q_DECLARE_PUBLIC(VehicleLayoutQueryModel)
0033 };
0034 }
0035 
0036 void VehicleLayoutQueryModelPrivate::doQuery()
0037 {
0038     Q_Q(VehicleLayoutQueryModel);
0039     if (!m_manager || !m_request.isValid()) {
0040         return;
0041     }
0042 
0043     // if the request already contains useful information, let's use those already
0044     q->beginResetModel();
0045     m_stopover = m_request.stopover();
0046     q->endResetModel();
0047     Q_EMIT q->contentChanged();
0048 
0049     setLoading(true);
0050     auto reply = m_manager->queryVehicleLayout(m_request);
0051     monitorReply(reply);
0052     QObject::connect(reply, &KPublicTransport::VehicleLayoutReply::finished, q, [reply, this]() {
0053         Q_Q(VehicleLayoutQueryModel);
0054         q->beginResetModel();
0055         m_stopover = reply->stopover();
0056         if (!m_stopover.platformLayout().isEmpty() && !m_stopover.vehicleLayout().isEmpty()
0057          && !m_stopover.vehicleLayout().hasPlatformPositions() && m_stopover.vehicleLayout().hasPlatformSectionNames()) {
0058             interpolatePlatformPositionsFromSectionName();
0059         }
0060         q->endResetModel();
0061         Q_EMIT q->contentChanged();
0062     });
0063 }
0064 
0065 void VehicleLayoutQueryModelPrivate::doClearResults()
0066 {
0067     m_stopover = {};
0068     Q_Q(VehicleLayoutQueryModel);
0069     Q_EMIT q->contentChanged();
0070 }
0071 
0072 void VehicleLayoutQueryModelPrivate::interpolatePlatformPositionsFromSectionName()
0073 {
0074     auto vehicle = m_stopover.vehicleLayout();
0075     auto vehicleSections = vehicle.takeSections();
0076     const auto startSection = vehicleSections.front().platformSectionName();
0077     const auto endSection = vehicleSections.back().platformSectionName();
0078 
0079     for (const auto &sec : m_stopover.platformLayout().sections()) {
0080         if (sec.name() == startSection) {
0081             interpolatePlatformPositionsFromSectionName(vehicleSections.begin(), vehicleSections.end());
0082             break;
0083         } else if (sec.name() == endSection) {
0084             interpolatePlatformPositionsFromSectionName(vehicleSections.rbegin(), vehicleSections.rend());
0085             break;
0086         }
0087     }
0088 
0089     vehicle.setSections(std::move(vehicleSections));
0090     m_stopover.setVehicleLayout(std::move(vehicle));
0091 }
0092 
0093 template<typename Iter>
0094 void VehicleLayoutQueryModelPrivate::interpolatePlatformPositionsFromSectionName(Iter begin, Iter end)
0095 {
0096     auto rangeBegin = begin, rangeEnd = begin;
0097     float minLength = 1.0;
0098     while (rangeBegin != end) {
0099         while (rangeEnd != end && (*rangeBegin).platformSectionName() == (*rangeEnd).platformSectionName()) {
0100             ++rangeEnd;
0101         }
0102 
0103         const auto platformIt = std::find_if(m_stopover.platformLayout().sections().begin(), m_stopover.platformLayout().sections().end(), [&rangeBegin](const auto &p) {
0104             return p.name() == (*rangeBegin).platformSectionName();
0105         });
0106         if (platformIt == m_stopover.platformLayout().sections().end()) {
0107             qWarning() << "Failed to find platform section" << (*rangeBegin).platformSectionName();
0108             return;
0109         }
0110 
0111         auto l = ((*platformIt).end() - (*platformIt).begin()) / std::distance(rangeBegin, rangeEnd);
0112         minLength = std::min(minLength, l);
0113 
0114         if (rangeEnd == end) { // trailing coaches, don't scale them to the full section
0115             l = minLength;
0116         }
0117 
0118         auto pos = (*platformIt).begin();
0119         for (auto it = rangeBegin; it != rangeEnd; ++it) {
0120             (*it).setPlatformPositionBegin(pos);
0121             (*it).setPlatformPositionEnd(pos + l);
0122             pos += l;
0123         }
0124 
0125         rangeBegin = rangeEnd;
0126     }
0127 
0128     // fix-up leading coaches to not fill up the entire platform section
0129     rangeEnd = std::find_if(begin, end, [&begin](const auto &p) {
0130         return p.platformSectionName() != (*begin).platformSectionName();
0131     });
0132     auto pos = (*std::prev(rangeEnd)).platformPositionEnd() - std::distance(begin, rangeEnd) * minLength;
0133     for (auto it = begin; it != rangeEnd; ++it) {
0134         (*it).setPlatformPositionBegin(pos);
0135         (*it).setPlatformPositionEnd(pos + minLength);
0136         pos += minLength;
0137     }
0138 }
0139 
0140 
0141 VehicleLayoutQueryModel::VehicleLayoutQueryModel(QObject* parent)
0142     : AbstractQueryModel(new VehicleLayoutQueryModelPrivate, parent)
0143 {
0144 }
0145 
0146 VehicleLayoutQueryModel::~VehicleLayoutQueryModel() = default;
0147 
0148 VehicleLayoutRequest VehicleLayoutQueryModel::request() const
0149 {
0150     Q_D(const VehicleLayoutQueryModel);
0151     return d->m_request;
0152 }
0153 
0154 void VehicleLayoutQueryModel::setRequest(const VehicleLayoutRequest &req)
0155 {
0156     Q_D(VehicleLayoutQueryModel);
0157     d->m_request = req;
0158     Q_EMIT requestChanged();
0159     d->query();
0160 }
0161 
0162 Vehicle VehicleLayoutQueryModel::vehicle() const
0163 {
0164     return stopover().vehicleLayout();
0165 }
0166 
0167 Platform VehicleLayoutQueryModel::platform() const
0168 {
0169     return stopover().platformLayout();
0170 }
0171 
0172 Stopover VehicleLayoutQueryModel::stopover() const
0173 {
0174     Q_D(const VehicleLayoutQueryModel);
0175     return d->m_stopover;
0176 }
0177 
0178 int VehicleLayoutQueryModel::rowCount(const QModelIndex &parent) const
0179 {
0180     Q_D(const VehicleLayoutQueryModel);
0181     if (parent.isValid()) {
0182         return 0;
0183     }
0184     return d->m_stopover.vehicleLayout().sections().size();
0185 }
0186 
0187 QVariant VehicleLayoutQueryModel::data(const QModelIndex &index, int role) const
0188 {
0189     Q_D(const VehicleLayoutQueryModel);
0190     if (!index.isValid()) {
0191         return {};
0192     }
0193 
0194     switch (role) {
0195         case VehicleSectionRole:
0196             return QVariant::fromValue(d->m_stopover.vehicleLayout().sections()[index.row()]);
0197     }
0198 
0199     return {};
0200 }
0201 
0202 QHash<int, QByteArray> VehicleLayoutQueryModel::roleNames() const
0203 {
0204     auto r = QAbstractListModel::roleNames();
0205     r.insert(VehicleSectionRole, "vehicleSection");
0206     return r;
0207 }
0208 
0209 #include "moc_vehiclelayoutquerymodel.moc"