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"