#include "csv-helpers.hpp" // Parse a single CSV line into a CSVRow struct CSVRow parse_csv_line(const std::string& line) { CSVRow row; std::istringstream ss(line); std::string token; std::getline(ss, row.icao, ','); std::getline(ss, row.r, ','); std::getline(ss, row.t, ','); std::getline(ss, token, ','); row.timestamp = std::stod(token); std::getline(ss, token, ','); row.lat = std::stod(token); std::getline(ss, token, ','); row.lon = std::stod(token); std::getline(ss, token, ','); row.alt = std::stod(token); std::getline(ss, token, ','); row.ias = std::stod(token); return row; } // Read the first row of a CSV file CSVRow read_first_csv_row(const std::string& filepath) { std::ifstream ifs(filepath); if (!ifs.is_open()) { throw std::runtime_error("Could not open file: " + filepath); } std::string line; std::getline(ifs, line); // Skip header std::getline(ifs, line); // Read first data row ifs.close(); return parse_csv_line(line); } // Read the last row of a CSV file CSVRow read_last_csv_row(const std::string& filepath) { std::ifstream ifs(filepath); if (!ifs.is_open()) { throw std::runtime_error("Could not open file: " + filepath); } std::string line; std::string last_line; std::getline(ifs, line); // Skip header while (std::getline(ifs, line)) { last_line = line; } ifs.close(); return parse_csv_line(last_line); } // Haversine distance calculation double haversine_distance(double lat1, double lon1, double lat2, double lon2) { const double R = 6371.0; // Earth radius in kilometers double dlat = (lat2 - lat1) * M_PI / 180.0; double dlon = (lon2 - lon1) * M_PI / 180.0; double a = std::sin(dlat / 2) * std::sin(dlat / 2) + std::cos(lat1 * M_PI / 180.0) * std::cos(lat2 * M_PI / 180.0) * std::sin(dlon / 2) * std::sin(dlon / 2); double c = 2 * std::atan2(std::sqrt(a), std::sqrt(1 - a)); return R * c; } // Filter by aircraft type bool aircraft_type_filter(const std::string& t) { // Return true if type starts with 'A[2-3]' or 'B7' or 'A19N' or 'E' if (t.size() < 2) return false; if (t[0] == 'A' && (t[1] == '2' || t[1] == '3')) return true; if (t[0] == 'B' && t[1] == '7') return true; if (t.find("A19N") != std::string::npos) return true; if (t[0] == 'E') return true; return false; } // Filter by proximity to known airports bool near_airport_filter(const double lat, const double lon, const double alt) { // Known airports (lon, lat) /* OSLO_GARDERMOEN_AIRPORT = (11.0859718, 60.1902228) BERGEN_FLESLAND_AIRPORT = (5.2201954, 60.293512) STAVANGER_SOLA_AIRPORT = (5.667972, 58.876667) UMEA_AIRPORT = (20.2840218, 63.7906151) GOTHENBURG_LANDVETTER_AIRPORT = (12.2850909, 57.669275) STOCKHOLM_ARLANDA_AIRPORT = (17.9185743, 59.6519444) TRONDHEIM_VAERNES_AIRPORT = (10.9110182, 63.4577224) TROMSO_LANGNES_AIRPORT = (18.910825, 69.683911) */ const std::vector> known_airports = { {11.0859718, 60.1902228}, {5.2201954, 60.293512}, {5.667972, 58.876667}, {20.2840218, 63.7906151}, {12.2850909, 57.669275}, {17.9185743, 59.6519444}, {10.9110182, 63.4577224}, {18.910825, 69.683911} }; for (const auto& airport : known_airports) { double distance = haversine_distance(lat, lon, airport.second, airport.first); if (distance <= AIRPORT_PROXIMITY_THRESHOLD_KM) { if (std::isnan(alt) || alt <= AIRPORT_MAX_ALTITUDE_FT) { return true; } } } return false; } // List all CSV files in a folder std::vector list_csv_files_in_folder(const std::string& folder_path) { std::vector csv_files; for (const auto& entry : std::filesystem::directory_iterator(folder_path)) { if (entry.path().extension() == ".csv") { csv_files.push_back(entry.path().string()); } else if (std::filesystem::is_directory(entry.path())) { auto nested_files = list_csv_files_in_folder(entry.path().string()); csv_files.insert(csv_files.end(), nested_files.begin(), nested_files.end()); } } return csv_files; }