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

0001 /*
0002     SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "line.h"
0008 #include "lineutil_p.h"
0009 #include "datatypes_p.h"
0010 #include "json_p.h"
0011 #include "mergeutil_p.h"
0012 #include "assetrepository_p.h"
0013 
0014 #include "knowledgedb/linemetadata.h"
0015 
0016 #include <QColor>
0017 #include <QDebug>
0018 #include <QUrl>
0019 
0020 using namespace KPublicTransport;
0021 
0022 namespace KPublicTransport {
0023 class LinePrivate : public QSharedData {
0024 public:
0025     Line::Mode mode = Line::Unknown;
0026     QString modeString;
0027     QString name;
0028     QColor color;
0029     QColor textColor;
0030     LineMetaData metaData;
0031 };
0032 
0033 class RoutePrivate : public QSharedData {
0034 public:
0035     Line line;
0036     QString direction;
0037     Location destination;
0038     QString name;
0039 };
0040 
0041 }
0042 
0043 KPUBLICTRANSPORT_MAKE_GADGET(Line)
0044 KPUBLICTRANSPORT_MAKE_PROPERTY(Line, QString, name, setName)
0045 KPUBLICTRANSPORT_MAKE_PROPERTY(Line, QColor, textColor, setTextColor)
0046 KPUBLICTRANSPORT_MAKE_PROPERTY(Line, Line::Mode, mode, setMode)
0047 KPUBLICTRANSPORT_MAKE_PROPERTY(Line, QString, modeString, setModeString)
0048 
0049 QColor Line::color() const
0050 {
0051     return d->metaData.color().isValid() ? d->metaData.color() : d->color;
0052 }
0053 
0054 void Line::setColor(const QColor &value)
0055 {
0056     d.detach();
0057     d->color = value;
0058 }
0059 
0060 bool Line::hasColor() const
0061 {
0062     return d->color.isValid() || d->metaData.color().isValid();
0063 }
0064 
0065 bool Line::hasTextColor() const
0066 {
0067     return d->textColor.isValid();
0068 }
0069 
0070 QString Line::logo() const
0071 {
0072     return AssetRepository::localFile(d->metaData.logoUrl()).toString(QUrl::FullyEncoded);
0073 }
0074 
0075 bool Line::hasLogo() const
0076 {
0077     return !logo().isEmpty();
0078 }
0079 
0080 QString Line::modeLogo() const
0081 {
0082     return AssetRepository::localFile(d->metaData.modeLogoUrl()).toString(QUrl::FullyEncoded);
0083 }
0084 
0085 bool Line::hasModeLogo() const
0086 {
0087     return !modeLogo().isEmpty();
0088 }
0089 
0090 bool Line::isSame(const Line &lhs, const Line &rhs)
0091 {
0092     if (!LineUtil::isCompatibleMode(lhs.mode(), rhs.mode())) {
0093         return false;
0094     }
0095 
0096     // ### not really the most efficient way of doing this...
0097     using namespace LineUtil;
0098     return isSameLineNameFuzzy(lhs.name(), rhs.name())
0099         || isSameLineNameFuzzy(QString(lhs.modeString() + QLatin1Char(' ') + lhs.name()).trimmed(), rhs.name())
0100         || isSameLineNameFuzzy(lhs.name(), QString(rhs.modeString() + QLatin1Char(' ') + rhs.name()).trimmed())
0101         || isSameLineNameFuzzy(QString(lhs.modeString() + QLatin1Char(' ') + lhs.name()).trimmed(), QString(rhs.modeString() + QLatin1Char(' ') + rhs.name()).trimmed());
0102 }
0103 
0104 static QColor mergeColor(const QColor &lhs, const QColor &rhs)
0105 {
0106     if (!lhs.isValid()) {
0107         return rhs;
0108     }
0109     if (!rhs.isValid()) {
0110         return lhs;
0111     }
0112 
0113     const auto lh = lhs.hue(), ls = lhs.saturation(), lv = lhs.value();
0114     const auto rh = rhs.hue(), rs = rhs.saturation(), rv = rhs.value();
0115     return std::tie(ls, lv, lh) < std::tie(rs, rv, rh) ? rhs : lhs;
0116 }
0117 
0118 Line Line::merge(const Line &lhs, const Line &rhs)
0119 {
0120     Line l(lhs);
0121 
0122     l.setModeString(MergeUtil::mergeString(lhs.modeString(), rhs.modeString()));
0123     l.setName(MergeUtil::mergeString(lhs.name(), rhs.name()));
0124     if (l.name().startsWith(l.modeString(), Qt::CaseInsensitive)) {
0125         l.setName(QStringView(l.name()).mid(l.modeString().size()).trimmed().toString());
0126     }
0127 
0128     l.setColor(mergeColor(lhs.color(), rhs.color()));
0129     if (!l.textColor().isValid() && rhs.textColor().isValid()) {
0130         l.setTextColor(rhs.textColor());
0131     }
0132     if (l.mode() == Unknown || (l.mode() == Train && rhs.mode() != Unknown)) {
0133         l.setMode(rhs.mode());
0134     }
0135     return l;
0136 }
0137 
0138 void Line::applyMetaData(const Location &location, bool download)
0139 {
0140     if (name().isEmpty() || !location.hasCoordinate()) {
0141         return;
0142     }
0143 
0144     d->metaData = LineMetaData::find(location.latitude(), location.longitude(), name(), mode());
0145 
0146     if (download && AssetRepository::instance()) {
0147         AssetRepository::instance()->download(d->metaData.logoUrl());
0148         AssetRepository::instance()->download(d->metaData.modeLogoUrl());
0149     }
0150 }
0151 
0152 QJsonObject Line::toJson(const Line &l)
0153 {
0154     auto obj = Json::toJson(l);
0155     if (l.mode() == Unknown) {
0156         obj.remove(QLatin1String("mode"));
0157     }
0158     return obj;
0159 }
0160 
0161 Line Line::fromJson(const QJsonObject &obj)
0162 {
0163     auto l = Json::fromJson<Line>(obj);
0164     return l;
0165 }
0166 
0167 
0168 KPUBLICTRANSPORT_MAKE_GADGET(Route)
0169 KPUBLICTRANSPORT_MAKE_PROPERTY(Route, Line, line, setLine)
0170 KPUBLICTRANSPORT_MAKE_PROPERTY(Route, Location, destination, setDestination)
0171 KPUBLICTRANSPORT_MAKE_PROPERTY(Route, QString, name, setName)
0172 
0173 QString Route::direction() const
0174 {
0175     if (d->direction.isEmpty() && !d->destination.isEmpty()) {
0176         return d->destination.name();
0177     }
0178     return d->direction;
0179 }
0180 
0181 void Route::setDirection(const QString &value)
0182 {
0183     d.detach();
0184     d->direction = value;
0185 }
0186 
0187 bool Route::isSame(const Route &lhs, const Route &rhs)
0188 {
0189     const auto matchingDirection = (!lhs.destination().isEmpty() && !rhs.destination().isEmpty() && Location::isSame(lhs.destination(), rhs.destination()))
0190         || Location::isSameName(lhs.direction(), rhs.direction());
0191 
0192     const auto matchingRouteName = lhs.name().size() < 3 || rhs.name().size() < 3 || lhs.name().endsWith(rhs.name()) || rhs.name().endsWith(lhs.name());
0193 
0194     return matchingDirection && matchingRouteName && Line::isSame(lhs.line(), rhs.line());
0195 }
0196 
0197 Route Route::merge(const Route &lhs, const Route &rhs)
0198 {
0199     Route r(lhs);
0200     r.setLine(Line::merge(lhs.line(), rhs.line()));
0201     r.setName(MergeUtil::mergeString(lhs.name(), rhs.name()));
0202     r.setDirection(MergeUtil::mergeString(lhs.direction(), rhs.direction()));
0203     r.setDestination(Location::merge(lhs.destination(), rhs.destination()));
0204     return r;
0205 }
0206 
0207 QJsonObject Route::toJson(const Route &r)
0208 {
0209     auto obj = Json::toJson(r);
0210     const auto lineObj = Line::toJson(r.line());
0211     if (!lineObj.empty()) {
0212         obj.insert(QLatin1String("line"), lineObj);
0213     }
0214     if (!r.destination().isEmpty()) {
0215         obj.insert(QLatin1String("destination"), Location::toJson(r.destination()));
0216     }
0217     return obj;
0218 }
0219 
0220 Route Route::fromJson(const QJsonObject &obj)
0221 {
0222     auto r = Json::fromJson<Route>(obj);
0223     r.setLine(Line::fromJson(obj.value(QLatin1String("line")).toObject()));
0224     r.setDestination(Location::fromJson(obj.value(QLatin1String("destination")).toObject()));
0225     return r;
0226 }
0227 
0228 #include "moc_line.cpp"