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

KDE's Doxygen guidelines are available online.