KPublicTransport

uicrailwaycoach.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "uicrailwaycoach.h"
7#include "datatypes/featureutil_p.h"
8
9#include <QDebug>
10
11using namespace KPublicTransport;
12
14{
15 // TODO handle different formatting of the coach number
16 if (coachNumber.size() > 4) {
17 return coachNumber.mid(2, 2);
18 }
19 return {};
20}
21
22static QStringView classificationDigit(QStringView coachNumber)
23{
24 // TODO handle different formatting
25 if (coachNumber.size() > 5) {
26 return coachNumber.mid(4, 1);
27 }
28 return {};
29}
30
31// see https://en.wikipedia.org/wiki/UIC_classification_of_railway_coaches
32struct {
33 const char prefix[5];
35 Feature::Type feature;
36 VehicleSection::Type type;
37 int deckCount;
38} static constexpr const class_prefix_table[] = {
39 { "AB", VehicleSection::FirstClass | VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
40 { "AR", VehicleSection::FirstClass, Feature::Restaurant, VehicleSection::PassengerCar, 1 },
41 { "A", VehicleSection::FirstClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
42 { "BR", VehicleSection::SecondClass, Feature::Restaurant, VehicleSection::PassengerCar, 1 },
43 { "B", VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
44 { "DAB", VehicleSection::FirstClass | VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 2 },
45 { "DA", VehicleSection::FirstClass, Feature::NoFeature, VehicleSection::UnknownType, 2 },
46 { "DB", VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 2 },
47 { "DD", VehicleSection::UnknownClass, Feature::NoFeature, VehicleSection::CarTransportCar, 2 },
48 { "WLAB", VehicleSection::FirstClass | VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::SleepingCar, 1 },
49 { "WLA", VehicleSection::FirstClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
50 { "WLB", VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
51 { "WR", VehicleSection::UnknownClass, Feature::Restaurant, VehicleSection::RestaurantCar, 1 },
52 { "KA", VehicleSection::FirstClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
53 { "KB", VehicleSection::SecondClass, Feature::NoFeature, VehicleSection::UnknownType, 1 },
54};
55
57{
58 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) {
59 return coachClassification.startsWith(QLatin1String(prefix.prefix));
60 });
61 if (it != std::end(class_prefix_table)) {
62 return (*it).classes;
63 }
64
65 const auto type = UicRailwayCoach::type(coachNumber, coachClassification);
66 if (type == VehicleSection::PassengerCar || type == VehicleSection::SleepingCar) {
67 const auto cls = classificationDigit(coachNumber);
68 if (!cls.empty()) {
69 switch (cls.at(0).cell()) {
70 case '1':
71 return VehicleSection::FirstClass;
72 case '2':
73 case '5':
74 return VehicleSection::SecondClass;
75 case '3':
76 return VehicleSection::FirstClass | VehicleSection::SecondClass;
77 }
78 }
79 }
80
81 return {};
82}
83
84// see https://de.wikipedia.org/wiki/Code_f%C3%BCr_das_Austauschverfahren
85struct {
86 const char prefix[3];
87 VehicleSection::Type type;
88 Feature::Type feature;
89} static constexpr const number_prefix_table[] = {
90 { "50", VehicleSection::PassengerCar, Feature::NoFeature },
91 { "70", VehicleSection::PassengerCar, Feature::AirConditioning },
92 { "71", VehicleSection::SleepingCar, Feature::NoFeature },
93 { "73", VehicleSection::PassengerCar, Feature::AirConditioning },
94 { "91", VehicleSection::Engine, Feature::NoFeature },
95 { "92", VehicleSection::Engine, Feature::NoFeature },
96};
97
98// see https://en.wikipedia.org/wiki/UIC_classification_of_railway_coaches
99struct UicClassificationSecondary {
100 const char code[4];
101 Feature::Type feature;
102 VehicleSection::Type type;
103 int deckCount;
104};
105
106// 54: Czech Republic
107static constexpr const UicClassificationSecondary secondary_54_table[] = {
108 { "b", Feature::WheelchairAccessible, VehicleSection::UnknownType, 1 },
109 { "c", Feature::NoFeature, VehicleSection::CouchetteCar, 1 },
110 { "d", Feature::BikeStorage, VehicleSection::UnknownType, 1 },
111 { "f", Feature::NoFeature, VehicleSection::ControlCar, 1 },
112 { "o", Feature::NoFeature, VehicleSection::UnknownType, 2 }, // ### could also be 't'?
113 { "z", Feature::AirConditioning, VehicleSection::PassengerCar, 1 },
114};
115
116// 80: Germany
117static constexpr const UicClassificationSecondary secondary_80_table[] = {
118 { "b", Feature::WheelchairAccessible, VehicleSection::UnknownType, 1 },
119 { "c", Feature::NoFeature, VehicleSection::CouchetteCar, 1 },
120 { "d", Feature::BikeStorage, VehicleSection::UnknownType, 1 },
121 { "f", Feature::NoFeature, VehicleSection::ControlCar, 1 },
122 { "k", Feature::Restaurant, VehicleSection::UnknownType, 1 },
123 { "p", Feature::AirConditioning, VehicleSection::PassengerCar, 1 },
124 { "q", Feature::NoFeature, VehicleSection::ControlCar, 1 },
125};
126
127// 81: Austria
128static constexpr const UicClassificationSecondary secondary_81_table[] = {
129 { "b", Feature::WheelchairAccessible, VehicleSection::UnknownType, 1 }, // TODO wheelchair accessible toilets specifically
130 { "c", Feature::NoFeature, VehicleSection::CouchetteCar, 1 },
131 { "f", Feature::NoFeature, VehicleSection::ControlCar, 1 },
132 { "p", Feature::NoFeature, VehicleSection::PassengerCar, 1 },
133 { "-s", Feature::NoFeature, VehicleSection::ControlCar, 1 },
134 { "-dl", Feature::NoFeature, VehicleSection::PassengerCar, 2 },
135 { "-ds", Feature::NoFeature, VehicleSection::ControlCar, 2 },
136};
137
138// 85: Switzerland
139static constexpr const UicClassificationSecondary secondary_85_table[] = {
140 { "c", Feature::NoFeature, VehicleSection::CouchetteCar, 1 },
141 { "r", Feature::Restaurant, VehicleSection::UnknownType, 1 },
142 { "t", Feature::NoFeature, VehicleSection::ControlCar, 1 },
143};
144
145// 87: France
146static constexpr const UicClassificationSecondary secondary_87_table[] = {
147 { "c", Feature::NoFeature, VehicleSection::CouchetteCar, 1 },
148 { "e", Feature::NoFeature, VehicleSection::UnknownType, 2 },
149 { "h", Feature::WheelchairAccessible, VehicleSection::UnknownType, 1 },
150 { "u", Feature::AirConditioning, VehicleSection::UnknownType, 1 },
151};
152
153struct {
154 const char country[3];
155 const UicClassificationSecondary *begin;
156 const UicClassificationSecondary *end;
157} static constexpr const secondary_tables[] = {
158 { "54", std::begin(secondary_54_table), std::end(secondary_54_table) },
159 { "80", std::begin(secondary_80_table), std::end(secondary_80_table) },
160 { "81", std::begin(secondary_81_table), std::end(secondary_81_table) },
161 { "85", std::begin(secondary_85_table), std::end(secondary_85_table) },
162 { "87", std::begin(secondary_87_table), std::end(secondary_87_table) },
163};
164
165int UicRailwayCoach::deckCount(QStringView coachNumber, QStringView coachClassification)
166{
167 int decks = 1;
168 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) {
169 return coachClassification.startsWith(QLatin1String(prefix.prefix));
170 });
171 if (it != std::end(class_prefix_table)) {
172 decks = std::max(decks, (*it).deckCount);
173 }
174
175 const auto country = UicRailwayCoach::countryCode(coachNumber);
176 for (const auto &tab : secondary_tables) {
177 if (country != QLatin1String(tab.country)) {
178 continue;
179 }
180 for (auto it = tab.begin; it != tab.end; ++it) {
181 if (coachClassification.contains(QLatin1String((*it).code))) {
182 decks = std::max(decks, (*it).deckCount);
183 }
184 }
185 }
186
187 return decks;
188}
189
190std::vector<Feature> UicRailwayCoach::features(QStringView coachNumber, QStringView coachClassification)
191{
192 std::vector<Feature> f;
193 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) {
194 return coachClassification.startsWith(QLatin1String(prefix.prefix));
195 });
196 if (it != std::end(class_prefix_table) && (*it).feature != Feature::NoFeature) {
197 FeatureUtil::add(f, Feature((*it).feature, Feature::Available));
198 }
199
200 const auto it2 = std::find_if(std::begin(number_prefix_table), std::end(number_prefix_table), [coachNumber](const auto &prefix) {
201 return coachNumber.startsWith(QLatin1String(prefix.prefix));
202 });
203 if (it2 != std::end(number_prefix_table) && (*it2).feature != Feature::NoFeature) {
204 FeatureUtil::add(f, Feature((*it2).feature, Feature::Available));
205 }
206
207 const auto country = UicRailwayCoach::countryCode(coachNumber);
208 for (const auto &tab : secondary_tables) {
209 if (country != QLatin1String(tab.country)) {
210 continue;
211 }
212 for (auto it = tab.begin; it != tab.end; ++it) {
213 if (coachClassification.contains(QLatin1String((*it).code)) && (*it).feature != Feature::NoFeature) {
214 FeatureUtil::add(f, Feature((*it).feature, Feature::Available));
215 }
216 }
217 }
218
219 return f;
220}
221
222VehicleSection::Type UicRailwayCoach::type(QStringView coachNumber, QStringView coachClassification)
223{
224 bool seenPassengerCar = false;
225 const auto it = std::find_if(std::begin(class_prefix_table), std::end(class_prefix_table), [coachClassification](const auto &prefix) {
226 return prefix.type != VehicleSection::UnknownType && coachClassification.startsWith(QLatin1String(prefix.prefix));
227 });
228 if (it != std::end(class_prefix_table)) {
229 if ((*it).type == VehicleSection::PassengerCar) {
230 seenPassengerCar = true;
231 } else {
232 return (*it).type;
233 }
234 }
235
236 const auto it2 = std::find_if(std::begin(number_prefix_table), std::end(number_prefix_table), [coachNumber](const auto &prefix) {
237 return prefix.type != VehicleSection::UnknownType && coachNumber.startsWith(QLatin1String(prefix.prefix));
238 });
239 if (it2 != std::end(number_prefix_table)) {
240 if ((*it2).type == VehicleSection::PassengerCar) {
241 seenPassengerCar = true;
242 } else {
243 return (*it2).type;
244 }
245 }
246
247 const auto country = UicRailwayCoach::countryCode(coachNumber);
248 for (const auto &tab : secondary_tables) {
249 if (country != QLatin1String(tab.country)) {
250 continue;
251 }
252 const auto it = std::find_if(tab.begin, tab.end, [coachClassification](const auto &prefix) {
253 return prefix.type != VehicleSection::UnknownType && coachClassification.contains(QLatin1String(prefix.code));
254 });
255 if (it != tab.end) {
256 if ((*it).type == VehicleSection::PassengerCar) {
257 seenPassengerCar = true;
258 } else {
259 return (*it).type;
260 }
261 }
262 }
263
264 return seenPassengerCar ? VehicleSection::PassengerCar : VehicleSection::UnknownType;
265}
An amenity, facility or other relevant property of a vehicle (train, bus, etc), vehicle part (e....
Definition feature.h:20
int deckCount(QStringView coachNumber, QStringView coachClassification)
Determine the number of decks from a UIC coachNumber and/or coachClassification.
QStringView countryCode(QStringView coachNumber)
Returns the UIC country code from coachNumber.
std::vector< Feature > features(QStringView coachNumber, QStringView coachClassification)
Determine coach features from a UIC coachNumber and/or coachClassification.
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
Determine the vehicle type from a UIC coachNumber and/or coachClassification.
VehicleSection::Classes coachClass(QStringView coachNumber, QStringView coachClassification)
Determine the coach class(es) from a UIC coachNumber and/or coachClassification.
Query operations and data types for accessing realtime public transport information from online servi...
QStringView mid(qsizetype start, qsizetype length) const const
bool contains(QChar c, Qt::CaseSensitivity cs) const const
qsizetype size() const const
bool startsWith(QChar ch) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 11:50:59 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.