// We tart it up with airframe and schedule data, trim out stale entries, and trim to fit box func getAirspaceForDisplay(c context.Context, bbox geo.LatlongBox) (airspace.Airspace, error) { a := airspace.NewAirspace() if err := a.JustAircraftFromMemcache(c); err != nil { return a, err } airframes := ref.NewAirframeCache(c) schedules := ref.NewScheduleCache(c) for k, aircraft := range a.Aircraft { age := time.Since(a.Aircraft[k].Msg.GeneratedTimestampUTC) if age > kMaxStaleDuration { delete(a.Aircraft, k) continue } if !bbox.SW.IsNil() && !bbox.Contains(aircraft.Msg.Position) { delete(a.Aircraft, k) continue } if af := airframes.Get(string(k)); af != nil { // Update entry in map to include the airframe data we just found aircraft.Airframe = *af a.Aircraft[k] = aircraft } if schedules != nil && time.Since(schedules.LastUpdated) < kMaxStaleScheduleDuration { if fs := schedules.Get(string(k)); fs != nil { aircraft.Schedule = fs.Identity.Schedule a.Aircraft[k] = aircraft } } } return a, nil }
// For clients, fetching from a pi/frontend via JSON func Fetch(client *http.Client, host string, bbox geo.LatlongBox) (*Airspace, error) { as := Airspace{} if host == "" { host = "fdb.serfr1.org" } url := fmt.Sprintf("http://%s/?json=1&%s", host, bbox.ToCGIArgs("box")) if resp, err := client.Get(url); err != nil { return nil, err } else if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("Bad status: %v", resp.Status) } else if err := json.NewDecoder(resp.Body).Decode(&as); err != nil { return nil, err } return &as, nil }
func (t Track) TimesInBox(b geo.LatlongBox) (s, e time.Time) { inside := false for _, tp := range t { if tp.AltitudeFeet == 0 || tp.SpeedKnots == 0 { continue } if !inside && b.Contains(tp.Latlong) { s = tp.TimestampUTC inside = true } else if inside { e = tp.TimestampUTC // keep overwriting e until we're outside (or we've landed) if !b.Contains(tp.Latlong) { break } } } return }
// WARNING: If we find something, we will annotate the track in-place func (track Track) LevelFlightAcrossBox(maxAbsDelta float64, box geo.LatlongBox, name string) *LevelFlightEvent { iStart, iEnd := 0, 0 for i, tp := range track { if iStart == 0 { if box.Contains(tp.Latlong) { iStart = i } } else { if !box.Contains(tp.Latlong) { iEnd = i - 1 break } } } if iStart == 0 || iEnd == 0 || iStart == iEnd { return nil } ev := LevelFlightEvent{ Start: track[iStart], End: track[iEnd], I: iStart, J: iEnd, } if math.Abs(ev.End.AltitudeFeet-ev.Start.AltitudeFeet) > maxAbsDelta { return nil } for i := iStart; i <= iEnd; i++ { track[i].X_Annotation = fmt.Sprintf("\n** LEVEL FLIGHT across %s", name) track[i].X_MapIcon = "red" } return &ev }