js8call/JS8_Logbook/CountryDat.cpp
2026-01-18 19:19:15 -06:00

180 lines
5.5 KiB
C++

/**
* @file CountryDat.cpp
* @brief Implementation of the CountryDat class for handling country data.
*/
/*
#Sov Mil Order of Malta: 15: 28: EU: 41.90: -12.43: -1.0: 1A:
#1A;
#Spratly Islands: 26: 50: AS: 9.88: -114.23: -8.0: 1S:
#1S,9M0,BV9S;
#Monaco: 14: 27: EU: 43.73: -7.40: -1.0: 3A:
#3A;
#Heard Island: 39: 68: AF: -53.08: -73.50: -5.0: VK0H:
#=VK0IR;
#Macquarie Island: 30: 60: OC: -54.60: -158.88: -10.0: VK0M:
#=VK0KEV;
#Cocos-Keeling: 29: 54: OC: -12.15: -96.82: -6.5: VK9C:
#AX9C,AX9Y,VH9C,VH9Y,VI9C,VI9Y,VJ9C,VJ9Y,VK9C,VK9Y,VL9C,VL9Y,VM9C,VM9Y,
#VN9C,VN9Y,VZ9C,VZ9Y,=VK9AA;
*/
#include "CountryDat.h"
#include "JS8_Main/Radio.h"
#include <QDebug>
#include <QFile>
#include <QTextStream>
/**
* @brief Initialize the CountryDat instance with the specified filename.
* @param filename The path to the country data file.
*/
void CountryDat::init(const QString filename) {
_filename = filename;
_data.clear();
}
/**
* @brief Extract the country name from a line in the country data file.
* @param line The line from which to extract the country name.
* @return The extracted country name.
*/
QString CountryDat::_extractName(const QString line) const {
int s1 = line.indexOf(':');
if (s1 >= 0) {
QString name = line.mid(0, s1);
return name;
}
return "";
}
/**
* @brief Remove substrings enclosed by specified brackets from a line.
* @param line The line to process.
* @param a The opening bracket.
* @param b The closing bracket.
*/
void CountryDat::_removeBrackets(QString &line, const QString a,
const QString b) const {
int s1 = line.indexOf(a);
while (s1 >= 0) {
int s2 = line.indexOf(b);
line = line.mid(0, s1) + line.mid(s2 + 1, -1);
s1 = line.indexOf(a);
}
}
/**
* @brief Extract prefixes from a line in the country data file.
* @param line The line from which to extract prefixes.
* @param more Output parameter indicating if more prefixes are expected.
* @return A list of extracted prefixes.
*/
QStringList CountryDat::_extractPrefix(QString &line, bool &more) const {
line = line.remove(" \n");
line = line.replace(" ", "");
_removeBrackets(line, "(", ")");
_removeBrackets(line, "[", "]");
_removeBrackets(line, "<", ">");
_removeBrackets(line, "~", "~");
int s1 = line.indexOf(';');
more = true;
if (s1 >= 0) {
line = line.mid(0, s1);
more = false;
}
QStringList r = line.split(',');
return r;
}
/**
* @brief Load country data from the specified file.
*/
void CountryDat::load() {
_data.clear();
_countryNames.clear(); // used by countriesWorked
QFile inputFile(_filename);
if (inputFile.open(QIODevice::ReadOnly)) {
QTextStream in(&inputFile);
while (!in.atEnd()) {
QString line1 = in.readLine();
if (!in.atEnd()) {
QString line2 = in.readLine();
QString name = _extractName(line1);
if (name.length() > 0) {
QString continent = line1.mid(36, 2);
QString principalPrefix = line1.mid(69, 4);
int i1 = principalPrefix.indexOf(":");
if (i1 > 0)
principalPrefix = principalPrefix.mid(0, i1);
name += "; " + principalPrefix + "; " + continent;
_countryNames << name;
bool more = true;
QStringList prefixs;
while (more) {
QStringList p = _extractPrefix(line2, more);
prefixs += p;
if (more)
line2 = in.readLine();
}
QString p;
foreach (p, prefixs) {
if (p.length() > 0)
_data.insert(p, name);
}
}
}
}
inputFile.close();
}
}
/**
* @brief Find the country name associated with a given callsign prefix.
* @param call The callsign prefix to search for.
* @return The corresponding country name, or an empty string if not found.
*/
QString CountryDat::find(QString call) const {
call = call.toUpper();
// check for exact match first
if (_data.contains("=" + call)) {
return fixup(_data.value("=" + call), call);
}
auto prefix = Radio::effective_prefix(call);
auto match_candidate = prefix;
while (match_candidate.size() >= 1) {
if (_data.contains(match_candidate)) {
return fixup(_data.value(match_candidate), prefix);
}
match_candidate = match_candidate.left(match_candidate.size() - 1);
}
return QString{};
}
/**
* @brief Apply special rules to fix up country names based on callsigns.
* @param country The initial country name.
* @param call The callsign to check for special rules.
* @return The potentially modified country name.
*/
QString CountryDat::fixup(QString country, QString const &call) const {
//
// deal with special rules that cty.dat does not cope with
//
// KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
if (call.startsWith("KG4") && call.size() != 5 && call.size() != 3) {
country.replace("Guantanamo Bay; KG4; NA", "United States; K; NA");
}
return country;
}