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(flight.departureTerminal().simplified());
43 flight.setArrivalTerminal(flight.arrivalTerminal().simplified());
44 flight.setFlightNumber(flight.flightNumber().simplified());
45
46 // arrival less than a day before departure is an indication of the extractor failing to detect day rollover
47 if (duration < std::chrono::seconds(0) && duration > std::chrono::days(-1)) {
48 flight.setArrivalTime(flight.arrivalTime().addDays(1));
49 }
50
51 return flight;
52}
53
54Airport FlightPostProcessor::processAirport(Airport airport, const std::vector<KnowledgeDb::IataCode> &codes) const
55{
56 // complete missing IATA codes
57 if (airport.iataCode().isEmpty() && codes.size() == 1) {
58 airport.setIataCode(codes[0].toString());
59 }
60
61 // complete missing geo coordinates, take whatever we have but don't trust that too much
62 auto geo = airport.geo();
63 if (codes.size() == 1) {
64 const auto coord = KnowledgeDb::coordinateForAirport(codes[0]);
65 if (coord.isValid() && (!geo.isValid() || LocationUtil::distance(geo.latitude(), geo.longitude(), coord.latitude, coord.longitude) > 5000)) {
66 geo.setLatitude(coord.latitude);
67 geo.setLongitude(coord.longitude);
68 airport.setGeo(geo);
69 }
70 }
71
72 // add country, if all candidates are from the same country
73 auto addr = airport.address();
74 if (addr.addressCountry().isEmpty() && codes.size() >= 1) {
75 const auto isoCode = KnowledgeDb::countryForAirport(codes[0]);
76 if (isoCode.isValid() && std::all_of(codes.begin(), codes.end(), [isoCode](const auto iataCode) { return KnowledgeDb::countryForAirport(iataCode) == isoCode; })) {
77 addr.setAddressCountry(isoCode.toString());
78 airport.setAddress(addr);
79 }
80 }
81
82 return ExtractorPostprocessorPrivate::processPlace(airport);
83}
84
85Airline FlightPostProcessor::processAirline(Airline airline) const
86{
87 airline.setName(airline.name().trimmed());
88 return airline;
89}
90
91QDateTime FlightPostProcessor::processFlightTime(QDateTime dt, const Flight &flight, const std::vector<KnowledgeDb::IataCode> &codes) const
92{
93 if (!dt.isValid()) {
94 return dt;
95 }
96
97 if (dt.date().year() <= 1970 && flight.departureDay().isValid()) { // we just have the time, but not the day
98 dt.setDate(flight.departureDay());
99 }
100
101 if ((dt.timeSpec() == Qt::TimeZone && dt.timeZone() != QTimeZone::utc()) || codes.empty()) {
102 return dt;
103 }
104
106 if (!tz.isValid() || !std::all_of(codes.begin(), codes.end(), [tz](const auto &iataCode) { return KnowledgeDb::timezoneForAirport(iataCode) == tz; })) {
107 return dt;
108 }
109
110 // prefer our timezone over externally provided UTC offset, if they match
111 if (dt.timeSpec() == Qt::OffsetFromUTC && tz.offsetFromUtc(dt) != dt.offsetFromUtc()) {
112 return dt;
113 }
114
115 if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) {
116 dt.setTimeZone(tz);
117 } else if (dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && dt.timeZone() == QTimeZone::utc())) {
118 dt = dt.toTimeZone(tz);
119 }
120
121 return dt;
122}
123
124void FlightPostProcessor::lookupAirportCodes(const Airport &airport, std::vector<KnowledgeDb::IataCode>& codes) const
125{
126 if (!airport.iataCode().isEmpty()) {
127 codes.push_back(KnowledgeDb::IataCode(airport.iataCode()));
128 return;
129 }
130
131 codes = KnowledgeDb::iataCodesFromName(airport.name());
132}
133
134void FlightPostProcessor::pickAirportByDistance(std::chrono::seconds duration, const std::vector<KnowledgeDb::IataCode>& startCodes, std::vector<KnowledgeDb::IataCode>& codes) const
135{
136 if (duration <= std::chrono::seconds(0) || startCodes.empty() || codes.size() <= 1) {
137 return;
138 }
139
140 // ensure we have coordinates for all start points
141 if (!std::all_of(startCodes.begin(), startCodes.end(), [](const auto code) { return KnowledgeDb::coordinateForAirport(code).isValid(); })) {
142 return;
143 }
144
145 for (auto it = codes.begin(); it != codes.end();) {
146 const auto destCoord = KnowledgeDb::coordinateForAirport(*it);
147 if (!destCoord.isValid()) {
148 continue;
149 }
150
151 bool outOfRange = true;
152 for (const auto startCode : startCodes) {
153 const auto startCoord = KnowledgeDb::coordinateForAirport(startCode);
154 const auto dist = LocationUtil::distance({startCoord.latitude, startCoord.longitude}, {destCoord.latitude, destCoord.longitude});
155 outOfRange = outOfRange && !FlightUtil::isPlausibleDistanceForDuration(dist, duration);
156 }
157 if (outOfRange) {
158 it = codes.erase(it);
159 } else {
160 ++it;
161 }
162 }
163}
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
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.
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-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:28:48 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.