File indexing completed on 2024-04-28 04:41:39
0001 /* 0002 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #include "uicrailwaycoach.h" 0007 0008 #include <QDebug> 0009 0010 using namespace KPublicTransport; 0011 0012 QStringView UicRailwayCoach::countryCode(QStringView coachNumber) 0013 { 0014 // TODO handle different formatting of the coach number 0015 if (coachNumber.size() > 4) { 0016 return coachNumber.mid(2, 2); 0017 } 0018 return {}; 0019 } 0020 0021 static QStringView classificationDigit(QStringView coachNumber) 0022 { 0023 // TODO handle different formatting 0024 if (coachNumber.size() > 5) { 0025 return coachNumber.mid(4, 1); 0026 } 0027 return {}; 0028 } 0029 0030 // see https://en.wikipedia.org/wiki/UIC_classification_of_railway_coaches 0031 struct { 0032 const char prefix[5]; 0033 VehicleSection::Classes classes; 0034 VehicleSection::Features features; 0035 VehicleSection::Type type; 0036 int deckCount; 0037 } static constexpr const class_prefix_table[] = { 0038 { "AB", VehicleSection::FirstClass | VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0039 { "AR", VehicleSection::FirstClass, VehicleSection::Restaurant, VehicleSection::PassengerCar, 1 }, 0040 { "A", VehicleSection::FirstClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0041 { "BR", VehicleSection::SecondClass, VehicleSection::Restaurant, VehicleSection::PassengerCar, 1 }, 0042 { "B", VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0043 { "DAB", VehicleSection::FirstClass | VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 2 }, 0044 { "DA", VehicleSection::FirstClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 2 }, 0045 { "DB", VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 2 }, 0046 { "DD", VehicleSection::UnknownClass, VehicleSection::NoFeatures, VehicleSection::CarTransportCar, 2 }, 0047 { "WLAB", VehicleSection::FirstClass | VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::SleepingCar, 1 }, 0048 { "WLA", VehicleSection::FirstClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0049 { "WLB", VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0050 { "WR", VehicleSection::UnknownClass, VehicleSection::Restaurant, VehicleSection::RestaurantCar, 1 }, 0051 { "KA", VehicleSection::FirstClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0052 { "KB", VehicleSection::SecondClass, VehicleSection::NoFeatures, VehicleSection::UnknownType, 1 }, 0053 }; 0054 0055 VehicleSection::Classes UicRailwayCoach::coachClass(QStringView coachNumber, QStringView coachClassification) 0056 { 0057 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) { 0058 return coachClassification.startsWith(QLatin1String(prefix.prefix)); 0059 }); 0060 if (it != std::end(class_prefix_table)) { 0061 return (*it).classes; 0062 } 0063 0064 const auto cls = classificationDigit(coachNumber); 0065 if (!cls.empty()) { 0066 switch (cls.at(0).cell()) { 0067 case '1': 0068 return VehicleSection::FirstClass; 0069 case '2': 0070 case '5': 0071 return VehicleSection::SecondClass; 0072 case '3': 0073 return VehicleSection::FirstClass | VehicleSection::SecondClass; 0074 } 0075 } 0076 0077 return {}; 0078 } 0079 0080 // see https://de.wikipedia.org/wiki/Code_f%C3%BCr_das_Austauschverfahren 0081 struct { 0082 const char prefix[3]; 0083 VehicleSection::Type type; 0084 VehicleSection::Features features; 0085 } static constexpr const number_prefix_table[] = { 0086 { "50", VehicleSection::PassengerCar, VehicleSection::NoFeatures }, 0087 { "70", VehicleSection::PassengerCar, VehicleSection::AirConditioning }, 0088 { "71", VehicleSection::SleepingCar, VehicleSection::NoFeatures }, 0089 { "73", VehicleSection::PassengerCar, VehicleSection::AirConditioning }, 0090 { "91", VehicleSection::Engine, VehicleSection::NoFeatures }, 0091 { "92", VehicleSection::Engine, VehicleSection::NoFeatures }, 0092 }; 0093 0094 // see https://en.wikipedia.org/wiki/UIC_classification_of_railway_coaches 0095 struct UicClassificationSecondary { 0096 const char code[4]; 0097 VehicleSection::Features features; 0098 VehicleSection::Type type; 0099 int deckCount; 0100 }; 0101 0102 // 54: Czech Republic 0103 static constexpr const UicClassificationSecondary secondary_54_table[] = { 0104 { "b", VehicleSection::WheelchairAccessible, VehicleSection::UnknownType, 1 }, 0105 { "c", VehicleSection::NoFeatures, VehicleSection::CouchetteCar, 1 }, 0106 { "d", VehicleSection::BikeStorage, VehicleSection::UnknownType, 1 }, 0107 { "f", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0108 { "o", VehicleSection::NoFeatures, VehicleSection::UnknownType, 2 }, // ### could also be 't'? 0109 { "z", VehicleSection::AirConditioning, VehicleSection::PassengerCar, 1 }, 0110 }; 0111 0112 // 80: Germany 0113 static constexpr const UicClassificationSecondary secondary_80_table[] = { 0114 { "b", VehicleSection::WheelchairAccessible, VehicleSection::UnknownType, 1 }, 0115 { "c", VehicleSection::NoFeatures, VehicleSection::CouchetteCar, 1 }, 0116 { "d", VehicleSection::BikeStorage, VehicleSection::UnknownType, 1 }, 0117 { "f", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0118 { "k", VehicleSection::Restaurant, VehicleSection::UnknownType, 1 }, 0119 { "p", VehicleSection::AirConditioning, VehicleSection::PassengerCar, 1 }, 0120 { "q", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0121 }; 0122 0123 // 81: Austria 0124 static constexpr const UicClassificationSecondary secondary_81_table[] = { 0125 { "b", VehicleSection::WheelchairAccessible, VehicleSection::UnknownType, 1 }, // TODO wheelchair accessible toilets specifically 0126 { "c", VehicleSection::NoFeatures, VehicleSection::CouchetteCar, 1 }, 0127 { "f", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0128 { "p", VehicleSection::NoFeatures, VehicleSection::PassengerCar, 1 }, 0129 { "-s", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0130 { "-dl", VehicleSection::NoFeatures, VehicleSection::PassengerCar, 2 }, 0131 { "-ds", VehicleSection::NoFeatures, VehicleSection::ControlCar, 2 }, 0132 }; 0133 0134 // 85: Switzerland 0135 static constexpr const UicClassificationSecondary secondary_85_table[] = { 0136 { "c", VehicleSection::NoFeatures, VehicleSection::CouchetteCar, 1 }, 0137 { "r", VehicleSection::Restaurant, VehicleSection::UnknownType, 1 }, 0138 { "t", VehicleSection::NoFeatures, VehicleSection::ControlCar, 1 }, 0139 }; 0140 0141 // 87: France 0142 static constexpr const UicClassificationSecondary secondary_87_table[] = { 0143 { "c", VehicleSection::NoFeatures, VehicleSection::CouchetteCar, 1 }, 0144 { "e", VehicleSection::NoFeatures, VehicleSection::UnknownType, 2 }, 0145 { "h", VehicleSection::WheelchairAccessible, VehicleSection::UnknownType, 1 }, 0146 { "u", VehicleSection::AirConditioning, VehicleSection::UnknownType, 1 }, 0147 }; 0148 0149 struct { 0150 const char country[3]; 0151 const UicClassificationSecondary *begin; 0152 const UicClassificationSecondary *end; 0153 } static constexpr const secondary_tables[] = { 0154 { "54", std::begin(secondary_54_table), std::end(secondary_54_table) }, 0155 { "80", std::begin(secondary_80_table), std::end(secondary_80_table) }, 0156 { "81", std::begin(secondary_81_table), std::end(secondary_81_table) }, 0157 { "85", std::begin(secondary_85_table), std::end(secondary_85_table) }, 0158 { "87", std::begin(secondary_87_table), std::end(secondary_87_table) }, 0159 }; 0160 0161 int UicRailwayCoach::deckCount(QStringView coachNumber, QStringView coachClassification) 0162 { 0163 int decks = 1; 0164 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) { 0165 return coachClassification.startsWith(QLatin1String(prefix.prefix)); 0166 }); 0167 if (it != std::end(class_prefix_table)) { 0168 decks = std::max(decks, (*it).deckCount); 0169 } 0170 0171 const auto country = UicRailwayCoach::countryCode(coachNumber); 0172 for (const auto &tab : secondary_tables) { 0173 if (country != QLatin1String(tab.country)) { 0174 continue; 0175 } 0176 for (auto it = tab.begin; it != tab.end; ++it) { 0177 if (coachClassification.contains(QLatin1String((*it).code))) { 0178 decks = std::max(decks, (*it).deckCount); 0179 } 0180 } 0181 } 0182 0183 return decks; 0184 } 0185 0186 VehicleSection::Features UicRailwayCoach::features(QStringView coachNumber, QStringView coachClassification) 0187 { 0188 VehicleSection::Features f = {}; 0189 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) { 0190 return coachClassification.startsWith(QLatin1String(prefix.prefix)); 0191 }); 0192 if (it != std::end(class_prefix_table)) { 0193 f |= (*it).features; 0194 } 0195 0196 const auto it2 = std::find_if(std::begin(number_prefix_table), std::end(number_prefix_table), [coachNumber](const auto &prefix) { 0197 return coachNumber.startsWith(QLatin1String(prefix.prefix)); 0198 }); 0199 if (it2 != std::end(number_prefix_table)) { 0200 f |= (*it2).features; 0201 } 0202 0203 const auto country = UicRailwayCoach::countryCode(coachNumber); 0204 for (const auto &tab : secondary_tables) { 0205 if (country != QLatin1String(tab.country)) { 0206 continue; 0207 } 0208 for (auto it = tab.begin; it != tab.end; ++it) { 0209 if (coachClassification.contains(QLatin1String((*it).code))) { 0210 f |= (*it).features; 0211 } 0212 } 0213 } 0214 0215 return f; 0216 } 0217 0218 VehicleSection::Type UicRailwayCoach::type(QStringView coachNumber, QStringView coachClassification) 0219 { 0220 bool seenPassengerCar = false; 0221 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) { 0222 return prefix.type != VehicleSection::UnknownType && coachClassification.startsWith(QLatin1String(prefix.prefix)); 0223 }); 0224 if (it != std::end(class_prefix_table)) { 0225 if ((*it).type == VehicleSection::PassengerCar) { 0226 seenPassengerCar = true; 0227 } else { 0228 return (*it).type; 0229 } 0230 } 0231 0232 const auto it2 = std::find_if(std::begin(number_prefix_table), std::end(number_prefix_table), [coachNumber](const auto &prefix) { 0233 return prefix.type != VehicleSection::UnknownType && coachNumber.startsWith(QLatin1String(prefix.prefix)); 0234 }); 0235 if (it2 != std::end(number_prefix_table)) { 0236 if ((*it2).type == VehicleSection::PassengerCar) { 0237 seenPassengerCar = true; 0238 } else { 0239 return (*it2).type; 0240 } 0241 } 0242 0243 const auto country = UicRailwayCoach::countryCode(coachNumber); 0244 for (const auto &tab : secondary_tables) { 0245 if (country != QLatin1String(tab.country)) { 0246 continue; 0247 } 0248 const auto it = std::find_if(tab.begin, tab.end, [coachClassification](const auto &prefix) { 0249 return prefix.type != VehicleSection::UnknownType && coachClassification.contains(QLatin1String(prefix.code)); 0250 }); 0251 if (it != tab.end) { 0252 if ((*it).type == VehicleSection::PassengerCar) { 0253 seenPassengerCar = true; 0254 } else { 0255 return (*it).type; 0256 } 0257 } 0258 } 0259 0260 return seenPassengerCar ? VehicleSection::PassengerCar : VehicleSection::UnknownType; 0261 }