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"