func FixupComplaint(c *types.Complaint, key *datastore.Key) { // 0. Snag the key, so we can refer to this object later c.DatastoreKey = key.Encode() // 1. GAE datastore helpfully converts timezones to UTC upon storage; fix that c.Timestamp = date.InPdt(c.Timestamp) // 2. Compute the flight details URL, if within 24 days age := date.NowInPdt().Sub(c.Timestamp) if age < time.Hour*24 { // c.AircraftOverhead.Fr24Url = c.AircraftOverhead.PlaybackUrl() c.AircraftOverhead.Fr24Url = "http://flightaware.com/live/flight/" + c.AircraftOverhead.FlightNumber // Or: http://flightaware.com/live/flight/UAL337/history/20151215/ [0655Z/KLAX/KSFO] // date is UTC of departure time; might be tricky to guess :/ } // 3. Compute distances, if we have an aircraft if c.AircraftOverhead.FlightNumber != "" { a := c.AircraftOverhead aircraftPos := geo.Latlong{a.Lat, a.Long} observerPos := geo.Latlong{c.Profile.Lat, c.Profile.Long} c.Dist2KM = observerPos.Dist(aircraftPos) c.Dist3KM = observerPos.Dist3(aircraftPos, a.Altitude) } }
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 }
func (fr *Fr24) FindOverhead(observerPos geo.Latlong, overhead *Aircraft, grabAnything bool) (debug string, err error) { debug = fmt.Sprintf("*** FindOverhead for %s, at %s\n", observerPos, date.NowInPdt()) // Create a bounding box that's ~40m square, centred on the input lat,long // This is a grievous fudge http://www.movable-type.co.uk/scripts/latlong.html lat_20miles := 0.3 long_20miles := 0.35 nearby := []Aircraft{} if err = fr.ListBbox(observerPos.Lat-lat_20miles, observerPos.Long-long_20miles, observerPos.Lat+lat_20miles, observerPos.Long+long_20miles, &nearby); err != nil { debug += fmt.Sprintf("Lookup error: %s\n", err) return } for i, a := range nearby { aircraftPos := geo.Latlong{a.Lat, a.Long} nearby[i].Dist = observerPos.Dist(aircraftPos) nearby[i].Dist3 = observerPos.Dist3(aircraftPos, a.Altitude) nearby[i].BearingFromObserver = observerPos.BearingTowards(aircraftPos) } sort.Sort(byDist3(nearby)) debug += "** nearby list:-\n" + DebugFlightList(nearby) filtered := filterAircraft(nearby) if len(filtered) == 0 { debug += "** all empty after filtering\n" return } debug += "** filtered:-\n" + DebugFlightList(filtered) if grabAnything { *overhead = filtered[0] debug += "** grabbed 1st\n" } else { // closest plane has to be within 12 km to be 'overhead', and it has // to be 4km away from the next-closest if filtered[0].Dist3 < 12.0 { if (len(filtered) == 1) || (filtered[1].Dist3-filtered[0].Dist3) > 4.0 { *overhead = filtered[0] debug += "** selected 1st\n" } else { debug += "** 2nd was too close to 1st\n" } } else { debug += "** 1st was too far away\n" } } return }
func (fr *Fr24) FindAllOverhead(observerPos geo.Latlong, overhead *flightid.Aircraft, grabAnything bool) (outAll []*Aircraft, outFilt []*Aircraft, debug string, err error) { outAll = []*Aircraft{} outFilt = []*Aircraft{} debug = fmt.Sprintf("*** FindOverhead for %s, at %s\n", observerPos, date.NowInPdt()) debug += fmt.Sprintf("* url: http://%s%s?array=1&bounds=%s\n", fr.host, kListUrlPath, "...") // Create a bounding box that's ~40m square, centred on the input lat,long // This is a grievous fudge http://www.movable-type.co.uk/scripts/latlong.html lat_20miles := 0.3 long_20miles := 0.35 nearby := []Aircraft{} if err = fr.ListBbox(observerPos.Lat-lat_20miles, observerPos.Long-long_20miles, observerPos.Lat+lat_20miles, observerPos.Long+long_20miles, &nearby); err != nil { debug += fmt.Sprintf("Lookup error: %s\n", err) return } for i, a := range nearby { // Hack for Surf Air; promote callsigns into (invalid_ flightnumbers, so they don't get stripped if a.FlightNumber == "" && regexp.MustCompile("^URF\\d+$").MatchString(a.Callsign) { nearby[i].FlightNumber = a.Callsign } else if a.FlightNumber != "" { if _, _, err := fdb.ParseIata(a.FlightNumber); err != nil { debug += "** saw bad flightnumber '" + a.FlightNumber + "' from fr24\n" nearby[i].FlightNumber = "" } } aircraftPos := geo.Latlong{a.Lat, a.Long} nearby[i].Dist = observerPos.Dist(aircraftPos) nearby[i].Dist3 = observerPos.Dist3(aircraftPos, a.Altitude) nearby[i].BearingFromObserver = observerPos.BearingTowards(aircraftPos) } sort.Sort(byDist3(nearby)) debug += "** nearby list:-\n" + DebugFlightList(nearby) filtered := filterAircraft(nearby) if len(filtered) == 0 { debug += "** all empty after filtering\n" return } for i, _ := range nearby { outAll = append(outAll, &nearby[i]) } for i, _ := range filtered { outFilt = append(outFilt, &filtered[i]) } debug += "** filtered:-\n" + DebugFlightList(filtered) if grabAnything { filtered[0].IntoFlightIdAircraft(overhead) debug += "** grabbed 1st\n" } else { // closest plane has to be within 12 km to be 'overhead', and it has // to be 4km away from the next-closest if filtered[0].Dist3 < 12.0 { if (len(filtered) == 1) || (filtered[1].Dist3-filtered[0].Dist3) > 4.0 { filtered[0].IntoFlightIdAircraft(overhead) debug += "** selected 1st\n" } else { debug += "** 2nd was too close to 1st\n" } } else { debug += "** 1st was too far away\n" } } return }