Commit d337aced authored by Volker Krause's avatar Volker Krause
Browse files

Add lookup table for VR (Finish railway) station codes

This doesn't cover codes containing 'Ä' or 'Ö' yet, which appear in the
official list, but for which we have no idea how they are encoded in the
corresponding barcode yet. As that's only a dozen or so in total, that's
not a big problem though.
parent 7ddc365b
......@@ -255,6 +255,17 @@ private Q_SLOTS:
QVERIFY(!station.coordinate.isValid());
QCOMPARE(station.timezone.toQTimeZone(), QTimeZone());
}
void testFinishStationCodeLookup()
{
auto station = KnowledgeDb::stationForVRStationCode(VRStationCode(QStringLiteral("HKI")));
QVERIFY(station.coordinate.isValid());
QCOMPARE(station.timezone.toQTimeZone(), QTimeZone("Europe/Helsinki"));
station = KnowledgeDb::stationForVRStationCode(VRStationCode(QStringLiteral("BLÄ")));
QVERIFY(!station.coordinate.isValid());
QCOMPARE(station.timezone.toQTimeZone(), QTimeZone());
}
};
QTEST_APPLESS_MAIN(KnowledgeDbTest)
......
......@@ -263,6 +263,9 @@ TrainStation ExtractorPostprocessorPrivate::processTrainStation(TrainStation sta
applyStationData(record, station);
} else if (id.startsWith(QLatin1String("benerail:")) && id.size() == 14) {
applyStationCountry(id.mid(9, 2).toUpper(), station);
} else if (id.startsWith(QLatin1String("vrfi:")) && id.size() >= 7 && id.size() <= 9) {
const auto record = KnowledgeDb::stationForVRStationCode(KnowledgeDb::VRStationCode(id.mid(5)));
applyStationData(record, station);
}
return processPlace(station);
......
......@@ -19,7 +19,7 @@
function readStationCode(bitarray, offset)
{
var s = "";
for (var i = 0; i < 3; ++i) {
for (var i = 0; i < 5; ++i) {
var n = bitarray.readNumberMSB(offset + i * 6, 6);
if (n != 36)
s += String.fromCharCode(n + 55);
......
......@@ -46,7 +46,7 @@ static bool operator<(const TrainStationDbGenerator::Station &lhs, const QUrl &r
bool TrainStationDbGenerator::generate(QIODevice *out)
{
// retrieve content from Wikidata
if (!fetchIBNR() || !fetchUIC() || !fetchGaresConnexions() || !fetchIndianRailwaysStationCode()) {
if (!fetchIBNR() || !fetchUIC() || !fetchGaresConnexions() || !fetchIndianRailwaysStationCode() || !fetchFinishStationCodes()) {
return false;
}
if (!fetchCountryInformation()) {
......@@ -72,6 +72,7 @@ namespace KnowledgeDb {
writeUICMap(out);
writeGareConnexionMap(out);
writeIndianRailwaysMap(out);
writeVRMap(out);
out->write(R"(
}
}
......@@ -231,6 +232,51 @@ bool TrainStationDbGenerator::fetchIndianRailwaysStationCode()
return true;
}
bool TrainStationDbGenerator::fetchFinishStationCodes()
{
const auto stationArray = WikiData::query(R"(
SELECT DISTINCT ?station ?stationLabel ?code ?coord ?ref WHERE {
?station (wdt:P31/wdt:P279*) wd:Q55488.
?station p:P296 ?codeStmt.
?codeStmt ps:P296 ?code.
?codeStmt prov:wasDerivedFrom ?refnode.
?refnode pr:P854 ?ref.
OPTIONAL { ?station wdt:P625 ?coord. }
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
} ORDER BY (?station))", "wikidata_trainstation_vrfi.json");
if (stationArray.isEmpty()) {
qWarning() << "Empty query result!";
return false;
}
for (const auto &stationData : stationArray) {
const auto stationObj = stationData.toObject();
const auto ref = stationObj.value(QLatin1String("ref")).toObject().value(QLatin1String("value")).toString();
if (!ref.contains(QLatin1String("rata.digitraffic.fi"), Qt::CaseInsensitive)) {
continue;
}
const auto uri = insertOrMerge(stationObj);
// TODO this filters 'Ä' and 'Ö' too, which seem to occur in a few cases?
const auto id = stationObj.value(QLatin1String("code")).toObject().value(QLatin1String("value")).toString().toUpper();
if (id.size() < 2 || id.size() > 4 || !Util::containsOnlyLetters(id)) {
++m_idFormatViolations;
qWarning() << "VR (Finland) station id format violation" << id << uri;
continue;
}
const auto it = m_vrfiMap.find(id);
if (it != m_vrfiMap.end() && (*it).second != uri) {
++m_idConflicts;
qWarning() << "Conflict on VR (Finland) station code" << id << uri << m_vrfiMap[id];
} else {
m_vrfiMap[id] = uri;
}
}
return true;
}
bool TrainStationDbGenerator::fetchCountryInformation()
{
const auto stationArray = WikiData::query(R"(
......@@ -441,6 +487,28 @@ R"(static constexpr const struct {
out->write("};\n\n");
}
void TrainStationDbGenerator::writeVRMap(QIODevice *out)
{
out->write("static constexpr const TrainStationIdIndex<VRStationCode> vrfiConnexionsId_table[] = {\n");
for (const auto &it : m_vrfiMap) {
const auto station = std::lower_bound(m_stations.begin(), m_stations.end(), it.second);
if (station == m_stations.end() || (*station).uri != it.second) {
continue;
}
out->write(" { VRStationCode{\"");
out->write(it.first.toUtf8());
for (int i = 0; i < 4 - it.first.size(); ++i) {
out->write("\\0");
}
out->write("\"}, TrainStationIndex{");
out->write(QByteArray::number((int)std::distance(m_stations.begin(), station)));
out->write("} }, // ");
out->write((*station).name.toUtf8());
out->write("\n");
}
out->write("};\n\n");
}
void TrainStationDbGenerator::printSummary()
{
qDebug() << "Generated database containing" << m_stations.size() << "train stations";
......@@ -448,6 +516,7 @@ void TrainStationDbGenerator::printSummary()
qDebug() << "UIC index:" << m_uicMap.size() << "elements";
qDebug() << "Gares & Connexions ID index:" << m_garesConnexionsIdMap.size() << "elements";
qDebug() << "Indian Railwaiys station code index:" << m_indianRailwaysMap.size() << "elements";
qDebug() << "VR (Finland) station code index:" << m_vrfiMap.size() << "elements";
qDebug() << "Identifier collisions:" << m_idConflicts;
qDebug() << "Identifier format violations:" << m_idFormatViolations;
qDebug() << "Coordinate conflicts:" << m_coordinateConflicts;
......
......@@ -54,6 +54,7 @@ private:
bool fetchUIC();
bool fetchGaresConnexions();
bool fetchIndianRailwaysStationCode();
bool fetchFinishStationCodes();
bool fetchCountryInformation();
QUrl insertOrMerge(const QJsonObject &obj, bool mergeOnly = false);
void processStations();
......@@ -62,6 +63,7 @@ private:
void writeUICMap(QIODevice *out);
void writeGareConnexionMap(QIODevice *out);
void writeIndianRailwaysMap(QIODevice *out);
void writeVRMap(QIODevice *out);
void printSummary();
std::vector<Station> m_stations;
......@@ -69,6 +71,7 @@ private:
std::map<uint32_t, QUrl> m_uicMap;
std::map<QString, QUrl> m_garesConnexionsIdMap;
std::map<QString, QUrl> m_indianRailwaysMap;
std::map<QString, QUrl> m_vrfiMap;
Timezones m_tzDb;
int m_idConflicts = 0;
......
......@@ -43,6 +43,17 @@ GaresConnexionsId::GaresConnexionsId(const QString& id)
setValue(fromChars(id.toUpper().toLatin1().constData()));
}
VRStationCode::VRStationCode(const QString &id)
{
if (id.size() < 2 || id.size() > 4) {
return;
}
char buffer[4];
memset(buffer, 0, 4);
memcpy(buffer, id.toUpper().toUtf8().constData(), id.size());
setValue(fromChars(buffer));
}
TrainStation KnowledgeDb::stationForIbnr(IBNR ibnr)
{
const auto ibnrIt = std::lower_bound(std::begin(ibnr_table), std::end(ibnr_table), ibnr);
......@@ -84,3 +95,13 @@ TrainStation KnowledgeDb::stationForIndianRailwaysStationCode(const QString &cod
return trainstation_table[(*it).stationIndex.value()];
}
TrainStation KnowledgeDb::stationForVRStationCode(VRStationCode vrStation)
{
const auto it = std::lower_bound(std::begin(vrfiConnexionsId_table), std::end(vrfiConnexionsId_table), vrStation);
if (it == std::end(vrfiConnexionsId_table) || (*it).stationId != vrStation) {
return {Coordinate{}, Timezone{}, CountryId{}};
}
return trainstation_table[(*it).stationIndex.value()];
}
......@@ -98,6 +98,30 @@ private:
}
};
/** VR (Finland) station codes.
* 2 to 4 letter uppercase alphabetic code.
*/
class VRStationCode : public UnalignedNumber<3>
{
public:
inline constexpr VRStationCode() = default;
inline explicit constexpr VRStationCode(const char s[4])
: UnalignedNumber<3>(fromChars(s))
{}
KITINERARY_EXPORT explicit VRStationCode(const QString &id);
private:
static inline constexpr uint32_t charVal(const char c)
{
// TODO in theory there's apparently also 'Ä' amd 'Ö'?
return c == '\0' ? 0 : c - '@';
}
static inline constexpr uint32_t fromChars(const char s[4])
{
return (charVal(s[0]) << 18) + (charVal(s[1]) << 12) + (charVal(s[2]) << 6) + charVal(s[3]);
}
};
/** Lookup train station data by IBNR. */
KITINERARY_EXPORT TrainStation stationForIbnr(IBNR ibnr);
......@@ -110,6 +134,9 @@ KITINERARY_EXPORT TrainStation stationForGaresConnexionsId(GaresConnexionsId gar
/** Lookup train station data by Indian Railways station code. */
KITINERARY_EXPORT TrainStation stationForIndianRailwaysStationCode(const QString &code);
/** Lookup train station data by VR (Finland) station code. */
KITINERARY_EXPORT TrainStation stationForVRStationCode(VRStationCode vrStation);
}
}
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment