diff --git a/src/blackmisc/aviation/aircrafticaocode.cpp b/src/blackmisc/aviation/aircrafticaocode.cpp index 23390340f..ad38733a8 100644 --- a/src/blackmisc/aviation/aircrafticaocode.cpp +++ b/src/blackmisc/aviation/aircrafticaocode.cpp @@ -290,7 +290,7 @@ namespace BlackMisc if (getEngineCount() != c.digitValue()) { return false; } } if (et == '*') { return true; } - const QString cet = getEngineType(); + const QString cet = this->getEngineType(); return cet.length() == 1 && cet.at(0) == et; } @@ -417,22 +417,61 @@ namespace BlackMisc return hasValidCombinedType() && hasDesignator() && hasValidWtc() && hasManufacturer(); } - bool CAircraftIcaoCode::matchesDesignator(const QString &designator) const + bool CAircraftIcaoCode::matchesDesignator(const QString &designator, int fuzzyMatch, int *result) const { + Q_ASSERT_X(fuzzyMatch >= -1 && fuzzyMatch <= 100, Q_FUNC_INFO, "fuzzyMatch range 0..100 or -1"); if (designator.isEmpty()) { return false; } - return designator.trimmed().toUpper() == this->m_designator; + const QString d = designator.trimmed().toUpper(); + if (fuzzyMatch >= 0) + { + const int r = fuzzyShortStringComparision(this->getDesignator(), d) >= fuzzyMatch; + if (result) { *result = r; } + return r >= fuzzyMatch; + } + else + { + const bool e = this->getDesignator() == d; + if (result) { *result = e ? 100 : 0; } + return e; + } } - bool CAircraftIcaoCode::matchesIataCode(const QString &iata) const + bool CAircraftIcaoCode::matchesIataCode(const QString &iata, int fuzzyMatch, int *result) const { + Q_ASSERT_X(fuzzyMatch >= -1 && fuzzyMatch <= 100, Q_FUNC_INFO, "fuzzyMatch range 0..100 or -1"); if (iata.isEmpty()) { return false; } - return iata.trimmed().toUpper() == this->m_iataCode; + const QString i = iata.trimmed().toUpper(); + if (fuzzyMatch >= 0) + { + const int r = fuzzyShortStringComparision(this->getIataCode(), i) >= fuzzyMatch; + if (result) { *result = r; } + return r >= fuzzyMatch; + } + else + { + const bool e = this->getIataCode() == i; + if (result) { *result = e ? 100 : 0; } + return e; + } } - bool CAircraftIcaoCode::matchesFamily(const QString &family) const + bool CAircraftIcaoCode::matchesFamily(const QString &family, int fuzzyMatch, int *result) const { + Q_ASSERT_X(fuzzyMatch >= -1 && fuzzyMatch <= 100, Q_FUNC_INFO, "fuzzyMatch range 0..100 or -1"); if (family.isEmpty()) { return false; } - return family.trimmed().toUpper() == this->m_family; + const QString f = family.trimmed().toUpper(); + if (fuzzyMatch >= 0) + { + const int r = fuzzyShortStringComparision(this->getFamily(), f) >= fuzzyMatch; + if (result) { *result = r; } + return r >= fuzzyMatch; + } + else + { + const bool e = this->getFamily() == f; + if (result) { *result = e ? 100 : 0; } + return e; + } } bool CAircraftIcaoCode::matchesDesignatorOrIata(const QString &icaoOrIata) const diff --git a/src/blackmisc/aviation/aircrafticaocode.h b/src/blackmisc/aviation/aircrafticaocode.h index 0063d55ef..7478719b4 100644 --- a/src/blackmisc/aviation/aircrafticaocode.h +++ b/src/blackmisc/aviation/aircrafticaocode.h @@ -251,13 +251,14 @@ namespace BlackMisc bool hasCompleteData() const; //! Matches designator string? - bool matchesDesignator(const QString &designator) const; + bool matchesDesignator(const QString &designator, int fuzzyMatch = -1, int *result = nullptr) const; //! Matches IATA string? - bool matchesIataCode(const QString &iata) const; + //! \remark IATA codes are only 3 characters, so using fuzzy search might yield bad results + bool matchesIataCode(const QString &iata, int fuzzyMatch = -1, int *result = nullptr) const; //! Matches family? - bool matchesFamily(const QString &family) const; + bool matchesFamily(const QString &family, int fuzzyMatch = -1, int *result = nullptr) const; //! Matches ICAO or IATA code bool matchesDesignatorOrIata(const QString &icaoOrIata) const; diff --git a/src/blackmisc/aviation/aircrafticaocodelist.cpp b/src/blackmisc/aviation/aircrafticaocodelist.cpp index 862090ca5..a8bfcb173 100644 --- a/src/blackmisc/aviation/aircrafticaocodelist.cpp +++ b/src/blackmisc/aviation/aircrafticaocodelist.cpp @@ -25,15 +25,32 @@ namespace BlackMisc CSequence(other) { } - CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDesignator(const QString &designator) const + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDesignator(const QString &designator, int fuzzySearch) const { - if (!CAircraftIcaoCode::isValidDesignator(designator)) { return CAircraftIcaoCodeList(); } + if (!fuzzySearch && !CAircraftIcaoCode::isValidDesignator(designator)) { return CAircraftIcaoCodeList(); } + if (fuzzySearch && designator.length() < 3) { return CAircraftIcaoCodeList(); } return this->findBy([&](const CAircraftIcaoCode & code) { - return code.matchesDesignator(designator); + return code.matchesDesignator(designator, fuzzySearch); }); } + CAircraftIcaoCode CAircraftIcaoCodeList::findBestFuzzyMatchOrDefault(const QString &designator, int cutoff) const + { + if (designator.length() < 3) { return CAircraftIcaoCode(); } + int best = 0; + int current = 0; + CAircraftIcaoCode found; + const QString d(designator.trimmed().toUpper()); + for (const CAircraftIcaoCode &code : * this) + { + if (!code.matchesDesignator(d, cutoff, ¤t)) { continue; } + if (current == 100.0) { return code; } + if (best < current) { found = code; } + } + return found; + } + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByValidDesignator() const { return this->findBy([](const CAircraftIcaoCode & code) @@ -88,21 +105,21 @@ namespace BlackMisc return icaosDesignator.isEmpty() ? icaosFamily : icaosDesignator; } - CAircraftIcaoCodeList CAircraftIcaoCodeList::findByIataCode(const QString &iata) const + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByIataCode(const QString &iata, int fuzzySearch) const { if (iata.isEmpty()) { return CAircraftIcaoCodeList(); } return this->findBy([&](const CAircraftIcaoCode & code) { - return code.matchesIataCode(iata); + return code.matchesIataCode(iata, fuzzySearch); }); } - CAircraftIcaoCodeList CAircraftIcaoCodeList::findByFamily(const QString &family) const + CAircraftIcaoCodeList CAircraftIcaoCodeList::findByFamily(const QString &family, int fuzzySearch) const { if (family.isEmpty()) { return CAircraftIcaoCodeList(); } return this->findBy([&](const CAircraftIcaoCode & code) { - return code.matchesFamily(family); + return code.matchesFamily(family, fuzzySearch); }); } @@ -338,22 +355,15 @@ namespace BlackMisc codes = this->findByIataCode(designator); if (!codes.isEmpty()) break; - // search by reduced length - if (designator.length() >= 4) - { - codes = this->findByDesignator(designator.left(4)); - if (!codes.isEmpty()) break; - } + // search fuzzy and restrict length + const CAircraftIcaoCode bestMatch = this->findBestFuzzyMatchOrDefault(designator.length() < 5 ? designator : designator.left(5), 70); + if (bestMatch.hasValidDesignator()) { return bestMatch; } // still empty, try to find by family codes = this->findByFamily(designator); if (!codes.isEmpty()) break; - // now try to find as ending - codes = this->findEndingWith(designator); - if (!codes.isEmpty()) break; - - // by any descriptionS + // by any description codes = this->findMatchingByAnyDescription(designator); } while (false); diff --git a/src/blackmisc/aviation/aircrafticaocodelist.h b/src/blackmisc/aviation/aircrafticaocodelist.h index 821630979..1b0b4593d 100644 --- a/src/blackmisc/aviation/aircrafticaocodelist.h +++ b/src/blackmisc/aviation/aircrafticaocodelist.h @@ -46,13 +46,16 @@ namespace BlackMisc CAircraftIcaoCodeList(const CSequence &other); //! Find by designator - CAircraftIcaoCodeList findByDesignator(const QString &designator) const; + CAircraftIcaoCodeList findByDesignator(const QString &designator, int fuzzySearch = -1) const; + + //! Find by designator + CAircraftIcaoCode findBestFuzzyMatchOrDefault(const QString &designator, int cutoff = 50) const; //! Find by IATA code - CAircraftIcaoCodeList findByIataCode(const QString &iata) const; + CAircraftIcaoCodeList findByIataCode(const QString &iata, int fuzzySearch = -1) const; //! Find by family - CAircraftIcaoCodeList findByFamily(const QString &family) const; + CAircraftIcaoCodeList findByFamily(const QString &family, int fuzzySearch = -1) const; //! Ones with a valid designator CAircraftIcaoCodeList findByValidDesignator() const; @@ -67,7 +70,8 @@ namespace BlackMisc CAircraftIcaoCodeList findByDesignatorIataOrFamily(const QString &icaoIataOrFamily) const; //! Find code ending with string, e.g. "738" finds "B738" - //! \remark many users use wrong ICAO designators, one typical mistake is "738" for "B737" + //! \remark many users use wrong ICAO designators, one typical mistake is "738" for "B737" + //! \remark consider to use findBestFuzzyMatchOrDefault CAircraftIcaoCodeList findEndingWith(const QString &icaoEnding) const; //! Find by manufacturer