Add historical flight track functionality

- Store track history with position, altitude, speed, and timestamp
- Automatic track point collection every 30 seconds when position changes
- API endpoint /api/aircraft/{hex}/history for individual aircraft tracks
- Frontend "Show History" button to display historical flight paths
- Click aircraft markers to show their historical track (dashed red line)
- Track cleanup: keep last 200 points per aircraft, 24-hour retention
- Add aircraft type badges in table view with color coding
- Start/end markers for historical tracks with timestamps

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-08-23 22:52:16 +02:00
commit 55710614da
6 changed files with 215 additions and 16 deletions

View file

@ -106,11 +106,28 @@ func (c *Dump1090Client) updateExistingAircraft(existing, update *parser.Aircraf
if update.Track != 0 {
existing.Track = update.Track
}
if update.Latitude != 0 {
if update.Latitude != 0 && update.Longitude != 0 {
existing.Latitude = update.Latitude
}
if update.Longitude != 0 {
existing.Longitude = update.Longitude
// Add to track history if position changed significantly
if c.shouldAddTrackPoint(existing, update) {
trackPoint := parser.TrackPoint{
Timestamp: update.LastSeen,
Latitude: update.Latitude,
Longitude: update.Longitude,
Altitude: update.Altitude,
Speed: update.GroundSpeed,
Track: update.Track,
}
existing.TrackHistory = append(existing.TrackHistory, trackPoint)
// Keep only last 200 points (about 3-4 hours at 1 point/minute)
if len(existing.TrackHistory) > 200 {
existing.TrackHistory = existing.TrackHistory[1:]
}
}
}
if update.VertRate != 0 {
existing.VertRate = update.VertRate
@ -121,6 +138,28 @@ func (c *Dump1090Client) updateExistingAircraft(existing, update *parser.Aircraf
existing.OnGround = update.OnGround
}
func (c *Dump1090Client) shouldAddTrackPoint(existing, update *parser.Aircraft) bool {
// Add track point if:
// 1. No history yet
if len(existing.TrackHistory) == 0 {
return true
}
lastPoint := existing.TrackHistory[len(existing.TrackHistory)-1]
// 2. At least 30 seconds since last point
if time.Since(lastPoint.Timestamp) < 30*time.Second {
return false
}
// 3. Position changed by at least 0.001 degrees (~100m)
latDiff := existing.Latitude - lastPoint.Latitude
lonDiff := existing.Longitude - lastPoint.Longitude
distanceChange := latDiff*latDiff + lonDiff*lonDiff
return distanceChange > 0.000001 // ~0.001 degrees squared
}
func (c *Dump1090Client) GetAircraftData() parser.AircraftData {
c.mutex.RLock()
defer c.mutex.RUnlock()
@ -201,9 +240,20 @@ func (c *Dump1090Client) cleanupStaleAircraft() {
defer c.mutex.Unlock()
cutoff := time.Now().Add(-2 * time.Minute)
trackCutoff := time.Now().Add(-24 * time.Hour)
for hex, aircraft := range c.aircraftMap {
if aircraft.LastSeen.Before(cutoff) {
delete(c.aircraftMap, hex)
} else {
// Clean up old track points (keep last 24 hours)
validTracks := make([]parser.TrackPoint, 0)
for _, point := range aircraft.TrackHistory {
if point.Timestamp.After(trackCutoff) {
validTracks = append(validTracks, point)
}
}
aircraft.TrackHistory = validTracks
}
}
}