swift
aircrafticaocodelist.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QJsonValue>
7 #include <Qt>
8 
10 #include "misc/setbuilder.h"
11 
12 SWIFT_DEFINE_SEQUENCE_MIXINS(swift::misc::aviation, CAircraftIcaoCode, CAircraftIcaoCodeList)
13 
14 namespace swift::misc::aviation
15 {
18  {}
19 
20  CAircraftIcaoCodeList::CAircraftIcaoCodeList(std::initializer_list<CAircraftIcaoCode> il)
22  {}
23 
24  CAircraftIcaoCodeList CAircraftIcaoCodeList::findByDesignator(const QString &designator, int fuzzySearch) const
25  {
26  if (!fuzzySearch && !CAircraftIcaoCode::isValidDesignator(designator)) { return {}; }
27  if (fuzzySearch && designator.length() < CAircraftIcaoCode::DesignatorMinLength) { return {}; }
28  return this->findBy(
29  [&](const CAircraftIcaoCode &code) { return code.matchesDesignator(designator, fuzzySearch); });
30  }
31 
33  {
34  if (designator.length() < CAircraftIcaoCode::DesignatorMinLength) { return {}; }
35  int best = 0;
36  int current = 0;
37  CAircraftIcaoCode found;
38  const QString d(designator.trimmed().toUpper());
39  for (const CAircraftIcaoCode &code : *this)
40  {
41  if (!code.matchesDesignator(d, cutoff, &current)) { continue; }
42  if (current == 100.0) { return code; }
43  if (best < current) { found = code; }
44  }
45  return found;
46  }
47 
49  {
50  return this->findBy([](const CAircraftIcaoCode &code) { return code.hasValidDesignator(); });
51  }
52 
54  {
55  return this->findBy([](const CAircraftIcaoCode &code) { return !code.hasValidDesignator(); });
56  }
57 
59  {
60  if (icaoOrIata.isEmpty()) { return {}; }
61  return this->findBy([&](const CAircraftIcaoCode &code) { return code.matchesDesignatorOrIata(icaoOrIata); });
62  }
63 
65  {
66  if (icaoIataOrFamily.isEmpty()) { return {}; }
67  return this->findBy(
68  [&](const CAircraftIcaoCode &code) { return code.matchesDesignatorIataOrFamily(icaoIataOrFamily); });
69  }
70 
72  {
73  const QString ends = icaoEnding.trimmed().toUpper();
74  if (ends.isEmpty()) { return {}; }
75  CAircraftIcaoCodeList icaosDesignator;
76  CAircraftIcaoCodeList icaosFamily;
77  for (const CAircraftIcaoCode &icao : *this)
78  {
79  if (icao.getDesignator().endsWith(ends)) { icaosDesignator.push_back(icao); }
80  else if (icao.getFamily().endsWith(ends)) { icaosFamily.push_back(icao); }
81  }
82  return icaosDesignator.isEmpty() ? icaosFamily : icaosDesignator;
83  }
84 
86  {
87  if (iata.isEmpty()) { return {}; }
88  return this->findBy([&](const CAircraftIcaoCode &code) { return code.matchesIataCode(iata, fuzzySearch); });
89  }
90 
92  {
93  if (family.isEmpty()) { return {}; }
94  return this->findBy([&](const CAircraftIcaoCode &code) { return code.matchesFamily(family, fuzzySearch); });
95  }
96 
98  {
99  if (manufacturer.isEmpty()) { return {}; }
100  return this->findBy([&](const CAircraftIcaoCode &code) {
101  return code.getManufacturer().startsWith(manufacturer, Qt::CaseInsensitive);
102  });
103  }
104 
106  {
107  if (description.isEmpty()) { return {}; }
108  return this->findBy([&](const CAircraftIcaoCode &code) {
109  return code.getModelDescription().startsWith(description, Qt::CaseInsensitive);
110  });
111  }
112 
114  {
115  return this->findBy([&](const CAircraftIcaoCode &code) { return code.matchesAnyDescription(description); });
116  }
117 
118  CAircraftIcaoCodeList CAircraftIcaoCodeList::findWithIataCode(bool removeWhenSameAsDesignator) const
119  {
120  return this->findBy([&](const CAircraftIcaoCode &code) {
121  if (!code.hasIataCode()) { return false; }
122  return !removeWhenSameAsDesignator || !code.isIataSameAsDesignator();
123  });
124  }
125 
126  CAircraftIcaoCodeList CAircraftIcaoCodeList::findWithFamily(bool removeWhenSameAsDesignator) const
127  {
128  return this->findBy([&](const CAircraftIcaoCode &code) {
129  if (!code.hasFamily()) { return false; }
130  return !removeWhenSameAsDesignator || !code.isFamilySameAsDesignator();
131  });
132  }
133 
135  {
136  return this->findBy([&](const CAircraftIcaoCode &code) { return (code.isMilitary() == military); });
137  }
138 
140  {
141  if (!CAircraftIcaoCode::isValidDesignator(designator)) { return {}; }
142  CAircraftIcaoCodeList codes(this->findByDesignator(designator));
143  if (codes.isEmpty()) { return {}; }
144  if (codes.size() < 2) { return codes.front(); }
146  return codes.front();
147  }
148 
150  {
151  if (designator.isEmpty()) { return false; }
152  return this->contains(&CAircraftIcaoCode::getDesignator, designator);
153  }
154 
156 
158  {
160  }
161 
163  {
166  }
167 
169  {
170  this->removeIf([](const CAircraftIcaoCode &icao) { return !icao.hasValidCombinedType(); });
171  }
172 
174 
175  QStringList CAircraftIcaoCodeList::toCompleterStrings(bool withIataCodes, bool withFamily, bool withCategory,
176  bool sort) const
177  {
178  QStringList c;
179  CAircraftIcaoCodeList icaos(*this);
180  if (sort) { icaos.sortByDesignatorAndRank(); }
181 
182  // 3 steps to get a proper sort order of the string list
183  for (const CAircraftIcaoCode &icao : std::as_const(icaos))
184  {
185  c.append(withCategory ? icao.getCombinedIcaoCategoryStringWithKey() : icao.getCombinedIcaoStringWithKey());
186  }
187 
188  if (withFamily)
189  {
190  const CAircraftIcaoCodeList icaosFamily = icaos.findWithFamily(true);
191  for (const CAircraftIcaoCode &icao : icaosFamily) { c.append(icao.getCombinedFamilyStringWithKey()); }
192  }
193 
194  if (withIataCodes)
195  {
196  icaos = icaos.findWithIataCode(true);
198  for (const CAircraftIcaoCode &icao : std::as_const(icaos))
199  {
200  c.append(icao.getCombinedIataStringWithKey());
201  }
202  }
203 
204  return c;
205  }
206 
208  {
210  for (const CAircraftIcaoCode &icao : *this)
211  {
212  if (noUnspecified && !icao.hasKnownDesignator()) { continue; }
213  const QString d(icao.getDesignator());
214  c.insert(d);
215  }
216  return c;
217  }
218 
220  {
222  for (const CAircraftIcaoCode &icao : *this)
223  {
224  if (noUnspecified && !icao.hasKnownDesignator()) { continue; }
225  const QString d(icao.getDesignatorDbKey());
226  c.insert(d);
227  }
228  return c;
229  }
230 
232  {
234  for (const CAircraftIcaoCode &icao : *this)
235  {
236  if (!icao.hasFamily()) { continue; }
237  const QString d(icao.getFamily());
238  c.insert(d);
239  }
240  return c;
241  }
242 
244  {
246  for (const CAircraftIcaoCode &icao : *this)
247  {
248  if (onlyKnownDesignators && !icao.hasKnownDesignator()) { continue; }
249  const QString m(icao.getManufacturer());
250  if (m.isEmpty()) { continue; }
251  c.insert(m); // checks if already contains m
252  }
253  return c;
254  }
255 
257  {
258  QMap<QString, int> count;
259  for (const CAircraftIcaoCode &icao : *this)
260  {
261  if (!icao.hasManufacturer()) { continue; }
262  count[icao.getManufacturer()]++;
263  }
264  return count;
265  }
266 
268  {
269  const QMap<QString, int> counts(countManufacturers());
270  if (counts.isEmpty()) return { {}, 0 };
271  const auto pair = *std::max_element(counts.keyValueBegin(), counts.keyValueEnd(),
272  [](const auto &a, const auto &b) { return a.second < b.second; });
273  return { pair.first, pair.second };
274  }
275 
277  const CAircraftCategoryList &categories,
278  bool ignoreIncompleteAndDuplicates,
279  CAircraftIcaoCodeList *inconsistent)
280  {
281  CAircraftIcaoCodeList codes;
282  for (const QJsonValue &value : array)
283  {
285  const int catId = icao.getCategory().getDbKey();
286  if (!categories.isEmpty() && catId >= 0)
287  {
288  const CAircraftCategory category = categories.findByKey(catId);
289  if (!category.isNull()) { icao.setCategory(category); }
290  }
291 
292  if (!icao.hasSpecialDesignator() && !icao.hasCompleteData())
293  {
294  if (ignoreIncompleteAndDuplicates) { continue; }
295  if (inconsistent)
296  {
297  inconsistent->push_back(icao);
298  continue;
299  }
300  }
301  if (icao.isDbDuplicate())
302  {
303  if (ignoreIncompleteAndDuplicates) { continue; }
304  if (inconsistent)
305  {
306  inconsistent->push_back(icao);
307  continue;
308  }
309  }
310  codes.push_back(icao);
311  }
312  return codes;
313  }
314 
316  {
317  if (icaoPattern.hasValidDbKey())
318  {
319  const int k = icaoPattern.getDbKey();
320  const CAircraftIcaoCode c(this->findByKey(k));
321  if (c.hasCompleteData()) { return c; }
322  }
323 
324  // get an initial set of data we can choose from
325  const QString designator(icaoPattern.getDesignator());
326  if (designator.isEmpty()) { return {}; }
327  CAircraftIcaoCodeList codes;
328  do {
329  codes = this->findByDesignator(designator);
330  if (!codes.isEmpty()) break;
331 
332  // now we search if the ICAO designator is actually an IATA code
333  codes = this->findByIataCode(designator);
334  if (!codes.isEmpty()) break;
335 
336  // search fuzzy and restrict length
337  const CAircraftIcaoCode bestMatch =
338  this->findBestFuzzyMatchOrDefault(designator.length() < 5 ? designator : designator.left(5), 70);
339  if (bestMatch.hasValidDesignator()) { return bestMatch; }
340 
341  // still empty, try to find by family
342  codes = this->findByFamily(designator);
343  if (!codes.isEmpty()) break;
344 
345  // by any description
346  codes = this->findMatchingByAnyDescription(designator);
347  }
348  while (false);
349 
350  if (codes.isEmpty()) { return icaoPattern; }
351  if (codes.size() == 1) { return codes.front(); }
352 
353  // further reduce by manufacturer
354  codes.sortByRank();
355  if (icaoPattern.hasManufacturer() &&
357  {
358  const QString m(icaoPattern.getManufacturer());
359  codes = codes.findByManufacturer(m);
360  if (codes.size() == 1) { return codes.front(); }
361 
362  // intentionally continue here
363  }
364 
365  // further reduce by IATA
366  if (icaoPattern.hasIataCode() && codes.contains(&CAircraftIcaoCode::getIataCode, icaoPattern.getIataCode()))
367  {
368  const QString i(icaoPattern.getIataCode());
369  codes = codes.findByIataCode(i);
370  if (codes.size() == 1) { return codes.front(); }
371 
372  // intentionally continue here
373  }
374 
375  // lucky punch on description?
376  if (icaoPattern.hasModelDescription() &&
378  {
379  // do not affect codes here, it might return no results
380  const QString d(icaoPattern.getModelDescription());
382  if (cm.size() == 1) { return cm.front(); }
383  }
384  return codes.frontOrDefault(); // sorted by rank
385  }
386 
388  {
389  CAircraftIcaoCodeList copy(*this);
391  CAircraftIcaoCodeList grouped; // will contain the entries with the best rank
392  QString designator;
393  QString manufacturer;
394  for (const CAircraftIcaoCode &code : std::as_const(copy))
395  {
396  if (code.getDesignator() != designator || code.getManufacturer() != manufacturer)
397  {
398  designator = code.getDesignator();
399  manufacturer = code.getManufacturer();
400  grouped.push_back(code);
401  }
402  }
403  return grouped;
404  }
405 } // namespace swift::misc::aviation
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void sortBy(K1 key1, Keys... keys)
In-place sort by some particular key(s).
Definition: sequence.h:576
const_reference frontOrDefault() const
Access the first element, or a default-initialized value if the sequence is empty.
Definition: sequence.h:239
CSequence findBy(Predicate p) const
Return a copy containing only those elements for which a given predicate returns true.
Definition: sequence.h:398
int removeIf(Predicate p)
Remove elements for which a given predicate returns true.
Definition: sequence.h:446
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
void sort(Predicate p)
In-place sort by a given comparator predicate.
Definition: sequence.h:560
Build a QSet more efficiently when calling insert() in a for loop.
Definition: setbuilder.h:25
void insert(const T &value)
Add an element to the set. Runs in amortized constant time.
Definition: setbuilder.h:29
Value object for aircraft categories.
Value object encapsulating a list of ICAO codes.
Value object for ICAO classification.
bool hasModelDescription() const
Has model description?
bool isIataSameAsDesignator() const
IATA code same as designator?
bool hasKnownDesignator() const
Has designator and designator is not "ZZZZ".
const QString & getFamily() const
Family (e.g. A350)
bool hasSpecialDesignator() const
Special designator.
bool matchesDesignatorOrIata(const QString &icaoOrIata) const
Matches ICAO or IATA code.
bool matchesDesignator(const QString &designator, int fuzzyMatch=-1, int *result=nullptr) const
Matches designator string?
bool matchesDesignatorIataOrFamily(const QString &icaoIataOrFamily) const
Matches ICAO, IATA, family?
bool hasIataCode() const
Has IATA code?
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
bool matchesIataCode(const QString &iata, int fuzzyMatch=-1, int *result=nullptr) const
Matches IATA string?
QString getDesignatorDbKey() const
Designator and DB key.
const QString & getModelDescription() const
Get IACO model description, e.g. "A-330-200".
const QString & getIataCode() const
IATA code.
bool hasValidDesignator() const
Valid aircraft designator?
static constexpr int DesignatorMinLength
designator length (min)
bool matchesAnyDescription(const QString &candidate) const
Matches any of the (unempty) descriptions.
bool isDbDuplicate() const
Is DB duplicate? This means a redundant ICAO DB entry.
static CAircraftIcaoCode fromDatabaseJson(const QJsonObject &json, const QString &prefix=QString())
From our database JSON format.
bool isFamilySameAsDesignator() const
Family same as designator?
const CAircraftCategory & getCategory() const
Get category.
bool matchesFamily(const QString &family, int fuzzyMatch=-1, int *result=nullptr) const
Matches family?
void setCategory(const CAircraftCategory &category)
Set category.
static bool isValidDesignator(const QString &designator)
Valid designator?
bool hasValidCombinedType() const
Combined type available?
const QString & getManufacturer() const
Get manufacturer, e.g. "Airbus".
Value object encapsulating a list of ICAO codes.
QSet< QString > allFamilies() const
All families, no duplicates.
CAircraftIcaoCodeList findByManufacturer(const QString &manufacturer) const
Find by manufacturer.
QSet< QString > allManufacturers(bool onlyKnownDesignators=true) const
All manufacturers.
static CAircraftIcaoCodeList fromDatabaseJson(const QJsonArray &array, const CAircraftCategoryList &categories, bool ignoreIncompleteAndDuplicates=true, CAircraftIcaoCodeList *inconsistent=nullptr)
From our database JSON format.
CAircraftIcaoCodeList findByInvalidDesignator() const
Ones with an invalid designator.
CAircraftIcaoCodeList groupByDesignatorAndManufacturer() const
Group by designator and manufacturer.
QStringList toCompleterStrings(bool withIataCodes=false, bool withFamily=false, bool withCategory=true, bool sort=true) const
For selection completion.
QSet< QString > allDesignatorsAndKey(bool noUnspecified=true) const
All ICAO codes and DB key, no duplicates.
CAircraftIcaoCodeList findWithFamily(bool removeWhenSameAsDesignator) const
Those with family.
CAircraftIcaoCodeList findWithIataCode(bool removeWhenSameAsDesignator) const
Those with IATA code.
CAircraftIcaoCodeList findByDescription(const QString &description) const
Find by model description.
CAircraftIcaoCodeList findByDesignator(const QString &designator, int fuzzySearch=-1) const
Find by designator.
CAircraftIcaoCodeList findByFamily(const QString &family, int fuzzySearch=-1) const
Find by family.
CAircraftIcaoCodeList findByMilitaryFlag(bool military) const
By military flag.
CAircraftIcaoCode findFirstByDesignatorAndRank(const QString &designator) const
Find by designator, then best match by rank.
CAircraftIcaoCodeList findMatchingByAnyDescription(const QString &description) const
Find matching by any model description.
void sortByDesignatorManufacturerAndRank()
Sort by designator first, then by manufacturer and rank.
void removeDuplicates()
Remove duplicates as marked by CAircraftIcaoCode::isDbDuplicate.
void sortByDesignatorAndRank()
Sort by designator first, then by rank.
QSet< QString > allDesignators(bool noUnspecified=true) const
All ICAO codes, no duplicates.
bool containsDesignator(const QString &designator) const
Contains designator?
CAircraftIcaoCode findBestFuzzyMatchOrDefault(const QString &designator, int cutoff=50) const
Find by designator.
CAircraftIcaoCodeList()=default
Default constructor.
void removeInvalidCombinedCodes()
Remove invalid combined codes.
CAircraftIcaoCode smartAircraftIcaoSelector(const CAircraftIcaoCode &icaoPattern) const
Best selection by given pattern, also searches IATA and family information.
CAircraftIcaoCodeList findByDesignatorOrIataCode(const QString &icaoOrIata) const
Find by ICAO/IATA code.
CAircraftIcaoCodeList findEndingWith(const QString &icaoEnding) const
Find code ending with string, e.g. "738" finds "B738".
CAircraftIcaoCodeList findByDesignatorIataOrFamily(const QString &icaoIataOrFamily) const
Find by ICAO/IATA code or family.
QPair< QString, int > maxCountManufacturer() const
Uses countManufacturers to find "most important" manufacturer.
CAircraftIcaoCodeList findByIataCode(const QString &iata, int fuzzySearch=-1) const
Find by IATA code.
CAircraftIcaoCodeList findByValidDesignator() const
Ones with a valid designator.
QMap< QString, int > countManufacturers() const
Count by manufacturer.
OBJ findByKey(KEYTYPE key, const OBJ &notFound=OBJ()) const
Object with key, notFound otherwise.
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
void append(QList< T > &&value)
bool isEmpty() const const
QMap< Key, T >::key_value_iterator keyValueBegin()
QMap< Key, T >::key_value_iterator keyValueEnd()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) &&
qsizetype length() const const
QString toUpper() const const
QString trimmed() const const
CaseInsensitive
#define SWIFT_DEFINE_SEQUENCE_MIXINS(Namespace, T, List)
Explicit template definition of mixins for a CSequence subclass.
Definition: sequence.h:63