func AirspaceToLocalizedAircraft(as *airspace.Airspace, pos geo.Latlong, elev float64) []Aircraft { ret := []Aircraft{} if as == nil { return ret } for _, ad := range as.Aircraft { tp := fdb.TrackpointFromADSB(ad.Msg) altitudeDelta := tp.Altitude - elev icaoid := string(ad.Msg.Icao24) icaoid = strings.TrimPrefix(icaoid, "EE") // fr24 airspaces use this prefix icaoid = strings.TrimPrefix(icaoid, "FF") // fa airspaces use this prefix a := Aircraft{ Dist: pos.DistKM(tp.Latlong), Dist3: pos.Dist3(tp.Latlong, altitudeDelta), BearingFromObserver: tp.Latlong.BearingTowards(pos), //Id: "someid", // This is set via an IdSpec string below Id2: icaoid, Lat: tp.Lat, Long: tp.Long, Track: tp.Heading, Altitude: tp.Altitude, Speed: tp.GroundSpeed, // Squawk string Radar: tp.ReceiverName, EquipType: ad.Airframe.EquipmentType, Registration: ad.Airframe.Registration, Epoch: float64(tp.TimestampUTC.Unix()), Origin: ad.Schedule.Origin, Destination: ad.Schedule.Destination, FlightNumber: ad.Schedule.IataFlight(), // Unknown float64 VerticalSpeed: tp.VerticalRate, Callsign: ad.Msg.Callsign, //"CAL123", //snap.Flight.Callsign, // Unknown2 float64 } // Even though we may be parsing an fr24 airspace, generate a skypi idspec (which may break) if icaoid != "" { a.Id = fdb.IdSpec{IcaoId: icaoid, Time: tp.TimestampUTC}.String() } // Hack for Surf Air; promote their callsigns into flightnumbers. // The goal is to allow them to get past the filter, and be printable etc. // There is probably a much better place for this kind of munging. But it needs to happen // regardless of the source of the airspace, so it can't be pushed upstream. (...?) if a.FlightNumber == "" && regexp.MustCompile("^URF\\d+$").MatchString(a.Callsign) { a.FlightNumber = a.Callsign } ret = append(ret, a) } sort.Sort(AircraftByDist3(ret)) return ret }