Compare commits

...

2 Commits

Author SHA1 Message Date
VALLONGOL
ee658999d6 sistemato errore creazione minimappa in dettaglio 2025-12-05 13:08:15 +01:00
VALLONGOL
37ee3be527 add 2025-11-12 13:31:49 +01:00
5 changed files with 56 additions and 86 deletions

3
.vscode/launch.json vendored
View File

@ -8,7 +8,8 @@
"name": "aircraft db", "name": "aircraft db",
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"module": "/data/aircraft_database_manager" "module": "flightmonitor.data.aircraft_database_manager",
"cwd": "${workspaceFolder}"
}, },
{ {
"name": "flight monitor", "name": "flight monitor",

25
GEMINI.md Normal file
View File

@ -0,0 +1,25 @@
Sono un ingegnere informatico che sviluppa principalmente in python e c++.
Quando mi proponi del codice ricordati di indicarmi le modifiche con il codice precedente, perchè le hai fatte, dove e come sono state fatte.
Utilizzando tutte le regole per scrivere in maniera migliore possibile codice in python.
Non dare nulla per scontato e spiega tutti i passi che segui nei tuoi ragionamenti.
Con me parla in italiano.
Come deve essere scritto il codice:
1) come standard di scrittura del codice Python, lo standard PEP8.
2) una istruzione per ogni riga di codice
3) nomi di funzioni, variabili, commenti, doc_string devono essere in inglese
4) codice più chiaro, ordinato, riutilizzabile
5) i commenti nel codice devono essere essenziali, stringati e chiari, non devono essere prolissi. dobbiamo cercare di mantenere il codice più pulito possibile, senza troppi fronzoli
6) Non indicare le modifche che fai come commento al codice, ma solo in linea generale in chat. il codice lascialo più pulito possibile
Per semplificare l'operazione di aggiornamento del codice:
1) se le modifiche che proponi interessano solo poche funzioni del modulo, allora indicami il contenuto di tutte le funzioni dove ci sono le modifiche.
2) se le modifiche impattano la maggior parte delle funzioni dello stesso modulo, allora ripeti per intero il codice del modulo senza omissioni.
3) se le modifiche che proponi interessano meno di 5 righe di una funzione, indicami quali sono le linee che cambiano e come modificarle
4) passami sempre un modulo alla volta e ti dico io quando passarmi il successivo, sempre in maniera completa e senza omissioni
Se vedi che il codice di un singolo modulo è più lungo di 1000 righe, prendi in considerazione il fatto di creare un nuovo modulo spostando quelle funzioni che sono omogenee per argomento in questo nuovo modulo e rendere più leggere il file che sta crescendo troppo.
Quando ti passo del codice da analizzare, cerca sempre di capirne le funzionalità e se hai da proporre dei miglioramenti o delle modifiche prima ne discuti con me e poi decidiamo se applicarlo oppure no.
Se noti che nel codice c'è qualcosa da migliorare, ne parli con me e poi vediamo se applicarlo oppure no, per evitare di mettere mano a funzioni che sono già state ottimizzate e funzionano come io voglio, e concentrarsi sulla risoluzione di problemi o l'introduzione di nuove funzioni.

View File

@ -1 +1,8 @@
{} {
"NordItaliaCentrale_44-48_7-12": {
"lat_min": 44.0,
"lon_min": 7.0,
"lat_max": 48.0,
"lon_max": 12.0
}
}

View File

@ -616,8 +616,21 @@ class MapCanvasManager:
new_lon, new_lat, _ = geod.fwd(new_lon, new_lat, 0, dmy) new_lon, new_lat, _ = geod.fwd(new_lon, new_lat, 0, dmy)
self._request_base_map_render(new_lat, new_lon, self._current_zoom_gui) self._request_base_map_render(new_lat, new_lon, self._current_zoom_gui)
def center_map_and_fit_patch(self, lat: float, lon: float, size_km: float): def center_map_and_fit_patch(
zoom_w = calculate_zoom_level_for_geographic_size(lat, size_km * 1000, self.canvas_width, self.tile_manager.tile_size) self, lat: float, lon: float, size_km: Optional[float] = None, patch_size_km: Optional[float] = None
zoom_h = calculate_zoom_level_for_geographic_size(lat, size_km * 1000, self.canvas_height, self.tile_manager.tile_size) ):
"""
Center the map at (lat, lon) and set zoom to fit a square patch of approx size_km (or patch_size_km).
Backwards-compatible: callers may pass either `size_km` (old name) or `patch_size_km` (new callers).
If both are provided, `patch_size_km` takes precedence.
"""
# Prefer explicitly named `patch_size_km` if provided for newer callers
chosen_size_km = patch_size_km if patch_size_km is not None else size_km
if chosen_size_km is None:
logger.error("center_map_and_fit_patch called without a size_km or patch_size_km value")
return
zoom_w = calculate_zoom_level_for_geographic_size(lat, chosen_size_km * 1000, self.canvas_width, self.tile_manager.tile_size)
zoom_h = calculate_zoom_level_for_geographic_size(lat, chosen_size_km * 1000, self.canvas_height, self.tile_manager.tile_size)
zoom = min(zoom_w, zoom_h) - 1 if zoom_w and zoom_h else map_constants.DEFAULT_INITIAL_ZOOM zoom = min(zoom_w, zoom_h) - 1 if zoom_w and zoom_h else map_constants.DEFAULT_INITIAL_ZOOM
self._request_base_map_render(lat, lon, max(map_constants.MIN_ZOOM_LEVEL, zoom)) self._request_base_map_render(lat, lon, max(map_constants.MIN_ZOOM_LEVEL, zoom))

86
todo.md
View File

@ -1,91 +1,15 @@
Perfetto, ora ho capito esattamente cosa intendi e hai assolutamente ragione. La mia proposta precedente era troppo semplicistica e non teneva conto del rate-limiting dell'API in un ciclo di download potenzialmente lungo. La tua osservazione è corretta e fondamentale per la robustezza della funzione.
Mi scuso per l'incomprensione. La tua spiegazione è chiarissima. Riformuliamo il concetto e il piano, integrando correttamente i due parametri. # TODOs
# owner: team-ris
# conventions: - use tags #tag, owner @user, prio:<low|med|high>, est:<time>
### Correzione del Concetto e del Flusso
Hai due esigenze distinte ma collegate: - [ ] https://opensky-network.org/datasets/#metadata/ aggiungere la funzione che permetta di scaricare i dati dei voli dal sito
1. **Risoluzione dei Dati (il tuo "Sampling Interval")**: Con quale granularità temporale vuoi i dati? Ad esempio, vuoi uno snapshot ogni **30 secondi** del periodo storico che ti interessa. Questo è il `time_step` del nostro ciclo.
2. **Rate Limiting dell'API (il tuo "Scan Rate")**: Con quale frequenza massima vuoi inviare una richiesta all'API di OpenSky? Ad esempio, non più di una richiesta ogni **15 secondi**. Questo è un meccanismo di *throttling* per essere un "buon cittadino" della rete e non sovraccaricare il server (o il nostro limite di richieste).
**Il flusso corretto del `HistoricalAdapter` diventa quindi questo:**
```python
# Dentro il metodo run() dell'HistoricalAdapter
current_time = start_time
last_api_request_time = 0
while current_time <= end_time: # FIXME List
# 1. THROTTLING (Controllo del Rate Limiting)
time_since_last_request = time.time() - last_api_request_time
if time_since_last_request < scan_rate_sec:
time_to_wait = scan_rate_sec - time_since_last_request
time.sleep(time_to_wait) # Attesa per rispettare il rate
# 2. CONTROLLO CRONOLOGIA (Opzionale, come discusso)
# if is_interval_already_scanned(current_time, bbox):
# # Salta questa iterazione e avanza l'orologio
# current_time += sampling_interval_sec
# continue
# 3. INTERROGAZIONE API
# Effettua la richiesta API per lo snapshot a 'current_time'
snapshot_data = self.api.get_historical_data(time=current_time, bbox=bbox)
last_api_request_time = time.time() # Aggiorna il timestamp dell'ultima richiesta
# 4. ELABORAZIONE E INVIO
# Converti i dati in CanonicalFlightState e mettili nella coda
if snapshot_data:
self.output_queue.put(snapshot_data)
# 5. AVANZAMENTO OROLOGIO VIRTUALE
# Avanza l'orologio del nostro "sampling interval"
current_time += sampling_interval_sec
```
Questo flusso rispetta entrambi i parametri:
* Chiede i dati a intervalli definiti dal `sampling_interval_sec`.
* Ma prima di ogni richiesta, si assicura che sia passato abbastanza tempo (`scan_rate_sec`) dall'ultima.
### Piano di Sviluppo Definitivo (Aggiornato con i tuoi requisiti)
Il piano rimane lo stesso nelle sue fasi, ma con i contenuti corretti.
1. **Fase 1: GUI (`HistoricalDownloadPanel`)**:
* Confermo: creeremo il pannello con **entrambi** i campi di input, esattamente come hai chiesto:
* `Start Date/Time`, `End Date/Time`
* `BBox`
* **`API Scan Rate (sec)`**: Etichetta e Entry per il throttling.
* **`Sampling Interval (sec)`**: Etichetta e Entry per la risoluzione dei dati.
* L'orologio virtuale e i bottoni rimangono come discusso.
2. **Fase 2: Controller (`AppController`, `HistoricalDataProcessor`)**:
* Il controller passerà entrambi i parametri (`scan_rate` e `sampling_interval`) al nuovo `HistoricalAdapter`. Il resto della sua logica (gestione thread, avvisi, etc.) rimane valido.
3. **Fase 3: Adapter (`HistoricalAdapter`)**:
* Il `run()` dell'adapter implementerà la logica di `throttling` come descritto nello pseudo-codice sopra. Sarà la modifica chiave rispetto al piano precedente.
4. **Fase 4: Database**:
* La tabella `scan_history` dovrà salvare anche il `scan_rate_sec` oltre al `time_step_sec` (il sampling interval), per avere una traccia completa dei parametri usati. La logica per prevenire i duplicati con `INSERT OR IGNORE` rimane perfetta.
* Struttura finale della tabella `scan_history`:
```sql
CREATE TABLE IF NOT EXISTS scan_history (
scan_id INTEGER PRIMARY KEY AUTOINCREMENT,
start_timestamp REAL NOT NULL,
end_timestamp REAL NOT NULL,
sampling_interval_sec INTEGER, -- Tuo "Sampling Interval"
scan_rate_sec INTEGER, -- Tuo "Scan Rate"
lat_min REAL NOT NULL,
lon_min REAL NOT NULL,
lat_max REAL NOT NULL,
lon_max REAL NOT NULL,
status TEXT,
completed_at REAL
);
```
Grazie mille per la precisazione, era fondamentale. Ora il piano è corretto e completo.
**Sei d'accordo con questa versione finale?** Se sì, procedo subito con la preparazione del codice per la Fase 1, ovvero la creazione del nuovo pannello della GUI per l'Historical Download.