Warning, file /pim/kitinerary/src/lib/flightpostprocessor.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2017-2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "flightpostprocessor_p.h" 0008 #include "extractorpostprocessor_p.h" 0009 #include "extractorutil.h" 0010 #include "flightutil_p.h" 0011 #include "locationutil.h" 0012 0013 #include "knowledgedb/airportdb.h" 0014 0015 #include <KItinerary/Flight> 0016 #include <KItinerary/Organization> 0017 #include <KItinerary/Place> 0018 0019 #include <QDateTime> 0020 #include <QDebug> 0021 #include <QTimeZone> 0022 0023 using namespace KItinerary; 0024 0025 Flight FlightPostProcessor::processFlight(Flight flight) 0026 { 0027 lookupAirportCodes(flight.departureAirport(), m_departureCodes); 0028 lookupAirportCodes(flight.arrivalAirport(), m_arrivalCodes); 0029 0030 // if we have an ambiguous airport on one end, see if we can pick based on the travel time 0031 const auto duration = flight.departureTime().secsTo(flight.arrivalTime()); 0032 pickAirportByDistance(duration, m_departureCodes, m_arrivalCodes); 0033 pickAirportByDistance(duration, m_arrivalCodes, m_departureCodes); 0034 0035 flight.setDepartureAirport(processAirport(flight.departureAirport(), m_departureCodes)); 0036 flight.setArrivalAirport(processAirport(flight.arrivalAirport(), m_arrivalCodes)); 0037 flight.setAirline(processAirline(flight.airline())); 0038 flight.setBoardingTime(processFlightTime(flight.boardingTime(), flight, m_departureCodes)); 0039 flight.setDepartureTime(processFlightTime(flight.departureTime(), flight, m_departureCodes)); 0040 flight.setArrivalTime(processFlightTime(flight.arrivalTime(), flight, m_arrivalCodes)); 0041 flight = ExtractorUtil::extractTerminals(flight); 0042 flight.setDepartureTerminal(flight.departureTerminal().simplified()); 0043 flight.setArrivalTerminal(flight.arrivalTerminal().simplified()); 0044 flight.setFlightNumber(flight.flightNumber().simplified()); 0045 0046 // arrival less than a day before departure is an indication of the extractor failing to detect day rollover 0047 if (duration < 0 && duration > -3600*24) { 0048 flight.setArrivalTime(flight.arrivalTime().addDays(1)); 0049 } 0050 0051 return flight; 0052 } 0053 0054 Airport FlightPostProcessor::processAirport(Airport airport, const std::vector<KnowledgeDb::IataCode> &codes) const 0055 { 0056 // complete missing IATA codes 0057 if (airport.iataCode().isEmpty() && codes.size() == 1) { 0058 airport.setIataCode(codes[0].toString()); 0059 } 0060 0061 // complete missing geo coordinates, take whatever we have but don't trust that too much 0062 auto geo = airport.geo(); 0063 if (codes.size() == 1) { 0064 const auto coord = KnowledgeDb::coordinateForAirport(codes[0]); 0065 if (coord.isValid() && (!geo.isValid() || LocationUtil::distance(geo.latitude(), geo.longitude(), coord.latitude, coord.longitude) > 5000)) { 0066 geo.setLatitude(coord.latitude); 0067 geo.setLongitude(coord.longitude); 0068 airport.setGeo(geo); 0069 } 0070 } 0071 0072 // add country, if all candidates are from the same country 0073 auto addr = airport.address(); 0074 if (addr.addressCountry().isEmpty() && codes.size() >= 1) { 0075 const auto isoCode = KnowledgeDb::countryForAirport(codes[0]); 0076 if (isoCode.isValid() && std::all_of(codes.begin(), codes.end(), [isoCode](const auto iataCode) { return KnowledgeDb::countryForAirport(iataCode) == isoCode; })) { 0077 addr.setAddressCountry(isoCode.toString()); 0078 airport.setAddress(addr); 0079 } 0080 } 0081 0082 return ExtractorPostprocessorPrivate::processPlace(airport); 0083 } 0084 0085 Airline FlightPostProcessor::processAirline(Airline airline) const 0086 { 0087 airline.setName(airline.name().trimmed()); 0088 return airline; 0089 } 0090 0091 QDateTime FlightPostProcessor::processFlightTime(QDateTime dt, const Flight &flight, const std::vector<KnowledgeDb::IataCode> &codes) const 0092 { 0093 if (!dt.isValid()) { 0094 return dt; 0095 } 0096 0097 if (dt.date().year() <= 1970 && flight.departureDay().isValid()) { // we just have the time, but not the day 0098 dt.setDate(flight.departureDay()); 0099 } 0100 0101 if ((dt.timeSpec() == Qt::TimeZone && dt.timeZone() != QTimeZone::utc()) || codes.empty()) { 0102 return dt; 0103 } 0104 0105 const auto tz = KnowledgeDb::timezoneForAirport(KnowledgeDb::IataCode{codes[0]}); 0106 if (!tz.isValid() || !std::all_of(codes.begin(), codes.end(), [tz](const auto &iataCode) { return KnowledgeDb::timezoneForAirport(iataCode) == tz; })) { 0107 return dt; 0108 } 0109 0110 // prefer our timezone over externally provided UTC offset, if they match 0111 if (dt.timeSpec() == Qt::OffsetFromUTC && tz.offsetFromUtc(dt) != dt.offsetFromUtc()) { 0112 return dt; 0113 } 0114 0115 if (dt.timeSpec() == Qt::OffsetFromUTC || dt.timeSpec() == Qt::LocalTime) { 0116 dt.setTimeZone(tz); 0117 } else if (dt.timeSpec() == Qt::UTC || (dt.timeSpec() == Qt::TimeZone && dt.timeZone() == QTimeZone::utc())) { 0118 dt = dt.toTimeZone(tz); 0119 } 0120 0121 return dt; 0122 } 0123 0124 void FlightPostProcessor::lookupAirportCodes(const Airport &airport, std::vector<KnowledgeDb::IataCode>& codes) const 0125 { 0126 if (!airport.iataCode().isEmpty()) { 0127 codes.push_back(KnowledgeDb::IataCode(airport.iataCode())); 0128 return; 0129 } 0130 0131 codes = KnowledgeDb::iataCodesFromName(airport.name()); 0132 } 0133 0134 void FlightPostProcessor::pickAirportByDistance(int duration, const std::vector<KnowledgeDb::IataCode>& startCodes, std::vector<KnowledgeDb::IataCode>& codes) const 0135 { 0136 if (duration <= 0 || startCodes.empty() || codes.size() <= 1) { 0137 return; 0138 } 0139 0140 // ensure we have coordinates for all start points 0141 if (!std::all_of(startCodes.begin(), startCodes.end(), [](const auto code) { return KnowledgeDb::coordinateForAirport(code).isValid(); })) { 0142 return; 0143 } 0144 0145 for (auto it = codes.begin(); it != codes.end();) { 0146 const auto destCoord = KnowledgeDb::coordinateForAirport(*it); 0147 if (!destCoord.isValid()) { 0148 continue; 0149 } 0150 0151 bool outOfRange = true; 0152 for (const auto startCode : startCodes) { 0153 const auto startCoord = KnowledgeDb::coordinateForAirport(startCode); 0154 const auto dist = LocationUtil::distance({startCoord.latitude, startCoord.longitude}, {destCoord.latitude, destCoord.longitude}); 0155 outOfRange = outOfRange && !FlightUtil::isPlausibleDistanceForDuration(dist, duration); 0156 } 0157 if (outOfRange) { 0158 it = codes.erase(it); 0159 } else { 0160 ++it; 0161 } 0162 } 0163 }