KItinerary

flightpostprocessor.cpp
1/*
2 SPDX-FileCopyrightText: 2017-2019 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "flightpostprocessor_p.h"
8#include "extractorpostprocessor_p.h"
9#include "extractorutil.h"
10#include "flightutil_p.h"
11#include "locationutil.h"
12
13#include "knowledgedb/airportdb.h"
14
15#include <KItinerary/Flight>
16#include <KItinerary/Organization>
17#include <KItinerary/Place>
18
19#include <QDateTime>
20#include <QDebug>
21#include <QTimeZone>
22
23using namespace KItinerary;
24
25Flight FlightPostProcessor::processFlight(Flight flight)
26{
27 lookupAirportCodes(flight.departureAirport(), m_departureCodes);
28 lookupAirportCodes(flight.arrivalAirport(), m_arrivalCodes);
29
30 // if we have an ambiguous airport on one end, see if we can pick based on the travel time
31 const std::chrono::seconds duration(flight.departureTime().secsTo(flight.arrivalTime()));
32 pickAirportByDistance(duration, m_departureCodes, m_arrivalCodes);
33 pickAirportByDistance(duration, m_arrivalCodes, m_departureCodes);
34
35 flight.setDepartureAirport(processAirport(flight.departureAirport(), m_departureCodes));
36 flight.setArrivalAirport(processAirport(flight.arrivalAirport(), m_arrivalCodes));
37 flight.setAirline(processAirline(flight.airline()));
38 flight.setBoardingTime(processFlightTime(flight.boardingTime(), flight, m_departureCodes));
39 flight.setDepartureTime(processFlightTime(flight.departureTime(), flight, m_departureCodes));
40 flight.setArrivalTime(processFlightTime(flight.arrivalTime(), flight, m_arrivalCodes));
41 flight = ExtractorUtil::extractTerminals(flight);
42 flight.setDepartureTerminal(StringUtil::simplifiedNoPlaceholder(flight.departureTerminal()));
43 flight.setArrivalTerminal(StringUtil::simplifiedNoPlaceholder(flight.arrivalTerminal()));
44 flight.setDepartureGate(StringUtil::simplifiedNoPlaceholder(flight.departureGate()));
45 flight.setFlightNumber(flight.flightNumber().simplified());
46
47 // arrival less than a day before departure is an indication of the extractor failing to detect day rollover
48 if (duration < std::chrono::seconds(0) && duration > std::chrono::days(-1)) {
49 flight.setArrivalTime(flight.arrivalTime().addDays(1));
50 }
51
52 return flight;
53}
54
55Airport FlightPostProcessor::processAirport(Airport airport, const std::vector<KnowledgeDb::IataCode> &codes) const
56{
57 // complete missing IATA codes
58 if (airport.iataCode().isEmpty() && codes.size() == 1) {
59 airport.setIataCode(codes[0].toString());
60 }
61
62 // complete missing geo coordinates, take whatever we have but don't trust that too much
63 auto geo = airport.geo();
64 if (codes.size() == 1) {
65 const auto coord = KnowledgeDb::coordinateForAirport(codes[0]);
66 if (coord.isValid() && (!geo.isValid() || LocationUtil::distance(geo.latitude(), geo.longitude(), coord.latitude, coord.longitude) > 5000)) {
67 geo.setLatitude(coord.latitude);
68 geo.setLongitude(coord.longitude);
69 airport.setGeo(geo);
70 }
71 }
72
73 // add country, if all candidates are from the same country
74 auto addr = airport.address();
75 if (addr.addressCountry().isEmpty() && codes.size() >= 1) {
76 const auto isoCode = KnowledgeDb::countryForAirport(codes[0]);
77 if (isoCode.isValid() && std::all_of(codes.begin(), codes.end(), [isoCode](const auto iataCode) { return KnowledgeDb::countryForAirport(iataCode) == isoCode; })) {
78 addr.setAddressCountry(isoCode.toString());
79 airport.setAddress(addr);
80 }
81 }
82
83 return ExtractorPostprocessorPrivate::processPlace(airport);
84}
85
86Airline FlightPostProcessor::processAirline(Airline airline) const
87{
88 airline.setName(airline.name().trimmed());
89 return airline;
90}
91
92QDateTime FlightPostProcessor::processFlightTime(QDateTime dt, const Flight &flight, const std::vector<KnowledgeDb::IataCode> &codes) const
93{
94 if (!dt.isValid()) {
95 return dt;
96 }
97
98 if (dt.date().year() <= 1970 && flight.departureDay().isValid()) { // we just have the time, but not the day
99 dt.setDate(flight.departureDay());
100 }
101
102 if ((dt.timeSpec() == Qt::TimeZone && dt.timeZone() != QTimeZone::utc()) || codes.empty()) {
103 return dt;
104 }
105
107 if (!tz.isValid() || !std::all_of(codes.begin(), codes.end(), [tz](const auto &iataCode) { return KnowledgeDb::timezoneForAirport(iataCode) == tz; })) {
108 return dt;
109 }
110
111 // prefer our timezone over externally provided UTC offset, if they match
112 if (dt.timeSpec() == Qt::OffsetFromUTC && tz.offsetFromUtc(dt) != dt.offsetFromUtc()) {
113 return dt;
114 }
115
116 if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) {
117 dt.setTimeZone(tz);
118 } else if (dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && dt.timeZone() == QTimeZone::utc())) {
119 dt = dt.toTimeZone(tz);
120 }
121
122 return dt;
123}
124
125void FlightPostProcessor::lookupAirportCodes(const Airport &airport, std::vector<KnowledgeDb::IataCode>& codes) const
126{
127 if (!airport.iataCode().isEmpty()) {
128 codes.push_back(KnowledgeDb::IataCode(airport.iataCode()));
129 return;
130 }
131
132 codes = KnowledgeDb::iataCodesFromName(airport.name());
133}
134
135void FlightPostProcessor::pickAirportByDistance(std::chrono::seconds duration, const std::vector<KnowledgeDb::IataCode>& startCodes, std::vector<KnowledgeDb::IataCode>& codes) const
136{
137 if (duration <= std::chrono::seconds(0) || startCodes.empty() || codes.size() <= 1) {
138 return;
139 }
140
141 // ensure we have coordinates for all start points
142 if (!std::all_of(startCodes.begin(), startCodes.end(), [](const auto code) { return KnowledgeDb::coordinateForAirport(code).isValid(); })) {
143 return;
144 }
145
146 for (auto it = codes.begin(); it != codes.end();) {
147 const auto destCoord = KnowledgeDb::coordinateForAirport(*it);
148 if (!destCoord.isValid()) {
149 continue;
150 }
151
152 bool outOfRange = true;
153 for (const auto startCode : startCodes) {
154 const auto startCoord = KnowledgeDb::coordinateForAirport(startCode);
155 const auto dist = LocationUtil::distance({startCoord.latitude, startCoord.longitude}, {destCoord.latitude, destCoord.longitude});
156 outOfRange = outOfRange && !FlightUtil::isPlausibleDistanceForDuration(dist, duration);
157 }
158 if (outOfRange) {
159 it = codes.erase(it);
160 } else {
161 ++it;
162 }
163 }
164}
A flight.
Definition flight.h:25
QDate departureDay
The scheduled day of departure.
Definition flight.h:46
char * toString(const EngineQuery &query)
Flight extractTerminals(Flight flight)
Move terminal indications from airport names to the correct fields.
QTimeZone timezoneForAirport(IataCode iataCode)
Returns the timezone the airport with IATA code iataCode is in.
Definition airportdb.cpp:40
Coordinate coordinateForAirport(IataCode iataCode)
Returns the geographical coordinates the airport with IATA code iataCode is in.
Definition airportdb.cpp:30
AlphaId< uint16_t, 3 > IataCode
IATA airport code.
Definition iatacode.h:17
std::vector< IataCode > iataCodesFromName(QStringView name)
Returns all possible IATA code candidates for the given airport name.
KnowledgeDb::CountryId countryForAirport(IataCode iataCode)
Returns the country the airport with IATA code iataCode is in.
Definition airportdb.cpp:50
GeoCoordinates geo(const QVariant &location)
Returns the geo coordinates of a given location.
int distance(const GeoCoordinates &coord1, const GeoCoordinates &coord2)
Computes the distance between to geo coordinates in meters.
QString simplifiedNoPlaceholder(const QString &s)
Same as QString::simplified() and dropping everything that just contains punctuation or dash characer...
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
bool isValid(int year, int month, int day)
int year() const const
QDateTime addDays(qint64 ndays) const const
QDate date() const const
bool isValid() const const
int offsetFromUtc() const const
qint64 secsTo(const QDateTime &other) const const
void setDate(QDate date)
void setTimeZone(const QTimeZone &toZone)
Qt::TimeSpec timeSpec() const const
QTimeZone timeZone() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
bool isEmpty() const const
QString simplified() const const
QString trimmed() const const
TimeZone
QTimeZone utc()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:52:35 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.