Files
flyradar/src/location_presets.cpp
Eiswolf-BG 36afd4ae75 v2.7.4: Named location presets and tappable nearest-airport ticker
Location presets can now optionally be given a name (e.g. "Home"),
shown in the overview instead of the raw coordinates. The
nearest-airport ticker on the Location Presets screen is now
tappable (shown with a thin green border) and adds that airport
directly as a new preset with its name and coordinates, using the
existing local airport database on the SD card - no internet lookup
needed. AirportLookup::findNearest() now also returns the found
airport's own lat/lon for this purpose.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:42:10 +02:00

141 lines
4.0 KiB
C++

#include "location_presets.h"
#include "config.h"
#include "sd_mutex.h"
#include "sd_storage.h"
#include <SD.h>
#include <cstring>
namespace LocationPresets {
namespace {
constexpr const char* PRESETS_FILE = "/Flightradar_cyd/locations.txt";
struct Preset {
double lat = 0;
double lon = 0;
// Optionaler, vom Nutzer vergebener Name (z.B. "Zuhause") oder vom
// naechstgelegenen Flughafen uebernommen - leer, wenn keiner gesetzt
// wurde. 16 Zeichen + Nullterminierung reichen fuer eine gut lesbare
// Zeile in der Preset-Liste.
char name[17] = {0};
};
Preset presets[MAX_PRESETS];
uint8_t presetCount = 0;
int8_t activeIdx = -1;
void saveToSd() {
if (!SdStorage::isMounted()) return;
SdMutex::Guard guard;
File f = SD.open(PRESETS_FILE, FILE_WRITE);
if (!f) return;
f.printf("active=%d\n", (int)activeIdx);
for (uint8_t i = 0; i < presetCount; i++) {
f.printf("%.6f,%.6f,%s\n", presets[i].lat, presets[i].lon, presets[i].name);
}
f.close();
}
void loadFromSd() {
presetCount = 0;
activeIdx = -1;
if (!SdStorage::isMounted()) return;
SdMutex::Guard guard;
if (!SD.exists(PRESETS_FILE)) return;
File f = SD.open(PRESETS_FILE, FILE_READ);
if (!f) return;
bool firstLine = true;
while (f.available()) {
String line = f.readStringUntil('\n');
line.trim();
if (line.length() == 0) continue;
if (firstLine) {
firstLine = false;
if (line.startsWith("active=")) {
activeIdx = (int8_t)line.substring(7).toInt();
continue;
}
}
int comma1 = line.indexOf(',');
if (comma1 < 0) continue;
if (presetCount >= MAX_PRESETS) break;
// Der Name ist ein optionales drittes Feld - aeltere,
// gespeicherte Presets (vor diesem Feature) haben nur
// "lat,lon" ohne zweites Komma, dann bleibt der Name leer.
int comma2 = line.indexOf(',', comma1 + 1);
presets[presetCount].lat = line.substring(0, comma1).toDouble();
if (comma2 < 0) {
presets[presetCount].lon = line.substring(comma1 + 1).toDouble();
presets[presetCount].name[0] = 0;
} else {
presets[presetCount].lon = line.substring(comma1 + 1, comma2).toDouble();
String nm = line.substring(comma2 + 1);
strncpy(presets[presetCount].name, nm.c_str(), sizeof(presets[presetCount].name) - 1);
}
presetCount++;
}
f.close();
if (activeIdx >= (int8_t)presetCount) activeIdx = -1;
}
}
void init() {
loadFromSd();
}
uint8_t count() { return presetCount; }
void getLatLon(uint8_t index, double& lat, double& lon) {
if (index >= presetCount) return;
lat = presets[index].lat;
lon = presets[index].lon;
}
String getName(uint8_t index) {
if (index >= presetCount) return String();
return String(presets[index].name);
}
bool addPreset(double lat, double lon, const String& name) {
if (presetCount >= MAX_PRESETS) return false;
presets[presetCount].lat = lat;
presets[presetCount].lon = lon;
strncpy(presets[presetCount].name, name.c_str(), sizeof(presets[presetCount].name) - 1);
presetCount++;
saveToSd();
return true;
}
void removePreset(uint8_t index) {
if (index >= presetCount) return;
for (uint8_t i = index; i < presetCount - 1; i++) {
presets[i] = presets[i + 1];
}
presetCount--;
presets[presetCount] = Preset{};
if (activeIdx == (int8_t)index) {
activeIdx = -1;
} else if (activeIdx > (int8_t)index) {
activeIdx--;
}
saveToSd();
}
int8_t activeIndex() { return activeIdx; }
void setActiveIndex(int8_t index) {
if (index >= (int8_t)presetCount) return;
activeIdx = index;
saveToSd();
}
}