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 "locationquerymodel.h" 0008 #include "abstractquerymodel_p.h" 0009 #include "logging.h" 0010 #include "../datatypes/locationutil_p.h" 0011 0012 #include <KPublicTransport/Attribution> 0013 #include <KPublicTransport/Location> 0014 #include <KPublicTransport/LocationReply> 0015 #include <KPublicTransport/Manager> 0016 0017 #include <QDebug> 0018 0019 using namespace KPublicTransport; 0020 0021 namespace KPublicTransport { 0022 class LocationQueryModelPrivate : public AbstractQueryModelPrivate 0023 { 0024 public: 0025 void doQuery() override; 0026 void doClearResults() override; 0027 void mergeResults(const std::vector<Location> &newLocations); 0028 bool isFiltered(const Location &loc) const; 0029 0030 std::vector<Location> m_locations; 0031 0032 LocationRequest m_request; 0033 0034 Q_DECLARE_PUBLIC(LocationQueryModel) 0035 }; 0036 } 0037 0038 void LocationQueryModelPrivate::doQuery() 0039 { 0040 Q_Q(LocationQueryModel); 0041 if (!m_manager || !m_request.isValid()) { 0042 return; 0043 } 0044 0045 setLoading(true); 0046 auto reply = m_manager->queryLocation(m_request); 0047 monitorReply(reply); 0048 QObject::connect(reply, &KPublicTransport::LocationReply::updated, q, [reply, this]() { 0049 mergeResults(reply->takeResult()); 0050 }); 0051 } 0052 0053 void LocationQueryModelPrivate::doClearResults() 0054 { 0055 m_locations.clear(); 0056 } 0057 0058 bool LocationQueryModelPrivate::isFiltered(const Location &loc) const 0059 { 0060 if (m_request.types() != Location::Place && loc.type() != Location::Place && (loc.type() & m_request.types()) == 0) { 0061 return true; 0062 } 0063 0064 if (m_request.hasCoordinate() && m_request.maximumDistance() > 0) { 0065 return Location::distance(m_request.latitude(), m_request.longitude(), loc.latitude(), loc.longitude()) > m_request.maximumDistance(); 0066 } 0067 0068 return false; 0069 } 0070 0071 void LocationQueryModelPrivate::mergeResults(const std::vector<Location> &newLocations) 0072 { 0073 Q_Q(LocationQueryModel); 0074 0075 for (const auto &loc : newLocations) { 0076 if (isFiltered(loc)) { 0077 continue; 0078 } 0079 0080 // lacking an actual useful ordering, we need to do a full search for merging 0081 // LocationUtil::sortLessThan provides an order, but proximity there does not imply 0082 // always all merge candidates are nearby unfortunately (e.g. in cases of native 0083 // language vs. English spelling in case of name searches). 0084 bool found = false; 0085 for (auto it = m_locations.begin(); it != m_locations.end(); ++it) { 0086 if (Location::isSame(*it, loc)) { 0087 *it = Location::merge(loc, *it); 0088 found = true; 0089 const auto row = std::distance(m_locations.begin(), it); 0090 const auto idx = q->index(row, 0); 0091 Q_EMIT q->dataChanged(idx, idx); 0092 break; 0093 } 0094 } 0095 if (found) { 0096 continue; 0097 } 0098 0099 auto it = std::lower_bound(m_locations.begin(), m_locations.end(), loc, [this](const auto &lhs, const auto &rhs) { 0100 return LocationUtil::sortLessThan(m_request, lhs, rhs); 0101 }); 0102 0103 const auto row = std::distance(m_locations.begin(), it); 0104 q->beginInsertRows({}, row, row); 0105 m_locations.insert(it, loc); 0106 q->endInsertRows(); 0107 } 0108 } 0109 0110 0111 LocationQueryModel::LocationQueryModel(QObject* parent) 0112 : AbstractQueryModel(new LocationQueryModelPrivate, parent) 0113 { 0114 } 0115 0116 LocationQueryModel::~LocationQueryModel() = default; 0117 0118 LocationRequest LocationQueryModel::request() const 0119 { 0120 Q_D(const LocationQueryModel); 0121 return d->m_request; 0122 } 0123 0124 void LocationQueryModel::setRequest(const LocationRequest &req) 0125 { 0126 Q_D(LocationQueryModel); 0127 d->m_request = req; 0128 Q_EMIT requestChanged(); 0129 d->query(); 0130 } 0131 0132 int LocationQueryModel::queryDelay() const 0133 { 0134 Q_D(const LocationQueryModel); 0135 return d->m_queryDelay.count(); 0136 } 0137 0138 void LocationQueryModel::setQueryDelay(int ms) 0139 { 0140 Q_D(LocationQueryModel); 0141 if (d->m_queryDelay == std::chrono::milliseconds(ms)) { 0142 return; 0143 } 0144 0145 d->m_queryDelay = std::chrono::milliseconds(ms); 0146 Q_EMIT queryDelayChanged(); 0147 } 0148 0149 int LocationQueryModel::rowCount(const QModelIndex &parent) const 0150 { 0151 Q_D(const LocationQueryModel); 0152 if (parent.isValid()) { 0153 return 0; 0154 } 0155 return d->m_locations.size(); 0156 } 0157 0158 QVariant LocationQueryModel::data(const QModelIndex &index, int role) const 0159 { 0160 Q_D(const LocationQueryModel); 0161 if (!index.isValid()) { 0162 return {}; 0163 } 0164 0165 switch (role) { 0166 case Qt::DisplayRole: 0167 return d->m_locations[index.row()].name(); 0168 case LocationRole: 0169 return QVariant::fromValue(d->m_locations[index.row()]); 0170 } 0171 0172 return {}; 0173 } 0174 0175 QHash<int, QByteArray> LocationQueryModel::roleNames() const 0176 { 0177 auto r = QAbstractListModel::roleNames(); 0178 r.insert(LocationRole, "location"); 0179 return r; 0180 } 0181 0182 const std::vector<Location>& LocationQueryModel::locations() const 0183 { 0184 Q_D(const LocationQueryModel); 0185 return d->m_locations; 0186 } 0187 0188 #include "moc_locationquerymodel.moc"