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"