From 3e75f88d804598041dbcf4f3139d91c283acdeef Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Thu, 6 Feb 2020 14:03:38 -0500 Subject: finish adding flags --- cmd/metar.go | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 cmd/metar.go (limited to 'cmd/metar.go') diff --git a/cmd/metar.go b/cmd/metar.go new file mode 100644 index 0000000..6355105 --- /dev/null +++ b/cmd/metar.go @@ -0,0 +1,223 @@ +package cmd + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + "bnbl.io/wx/metar" + "github.com/spf13/cobra" +) + +// wx metar --past 1h kbos +// wx metar --past 3h kbos kbed +// wx metar --past 1h @MA +// wx metar --since yesterday --until now kbos +// wx metar --since yesterday --until now --rect 0,0,1,1 +// wx metar --past 1h --rect 0,0,1,1 +// wx metar --past 1h --radial 90,0,0 +// wx metar --past 6h --most-recent-by-station=constraint --path-dist 50 --waypoint ksea --waypoint kbos --min-dist 3 + +type relTime struct { + t time.Time +} + +func (t *relTime) String() string { + return t.t.String() +} + +func (t *relTime) Set(s string) (err error) { + // first, see if s is a duration that we can use relative to now, e.g. + // "--since 3d" means since 3 days ago. + if d, err := time.ParseDuration(s); err == nil { + t.t = time.Now().Add(-1 * d) + return nil + } + + // next, try to parse it in a series of formats + t.t, err = time.Parse("2006-01-02T15:04:05", s) + return +} + +func (t *relTime) Type() string { + return "relative time" +} + +type rectangle struct { + minLat, minLon, maxLat, maxLon float64 +} + +func (r *rectangle) String() string { + return fmt.Sprintf("%f,%f,%f,%f", r.minLat, r.minLon, r.maxLat, r.maxLon) +} + +func (r *rectangle) Set(s string) error { + n, err := fmt.Sscanf(s, "%f,%f,%f,%f", &r.minLat, &r.minLon, &r.maxLat, &r.maxLon) + if err != nil { + return err + } + if n != 4 { + return fmt.Errorf("excpected 4 values but got %d", n) + } + return nil +} + +func (r *rectangle) Type() string { + return "rect" +} + +func (r *rectangle) IsZero() bool { + return r.minLat == 0 && r.minLon == 0 && r.maxLat == 0 && r.maxLon == 0 +} + +type ring struct { + radius, lat, lon float64 +} + +func (r *ring) String() string { + return fmt.Sprintf("%f,%f,%f", r.radius, r.lat, r.lon) +} + +func (r *ring) Set(s string) error { + n, err := fmt.Sscanf(s, "%f,%f,%f", &r.radius, &r.lat, &r.lon) + if err != nil { + return err + } + if n != 3 { + return fmt.Errorf("expected a radius, lat, and lon, but got %d values", n) + } + return nil +} + +func (r *ring) Type() string { + return "radial" +} + +func (r *ring) IsZero() bool { + return r.radius == 0 && r.lat == 0 && r.lon == 0 +} + +var ( + since, until relTime + past time.Duration + mostRecentByStation string + mostRecent bool + rect rectangle + radial ring + pathDist float64 + waypoints []string + minDist float64 +) + +func init() { + rootCmd.AddCommand(metarCmd) + + // time constraints + metarCmd.Flags().Var( + &since, "since", + `Starting time for METARs (e.g. "2020-01-01T01:31:00" or "3h"), used in +conjunction with --until`) + metarCmd.Flags().Var( + &until, "until", + `Ending time for METARs (e.g. "2020-01-01T01:31:00" or "3h"), used in +conjunction with --since`) + metarCmd.Flags().DurationVar( + &past, "past", 0, + `Duration to retrieve METARs since (e.g. "3h")`) + + // location constraints + metarCmd.Flags().Var( + &rect, "rect", + `A lat-lon rect to search within (minLat,minLon,maxLat,maxLon) e.g. +"40,-71,41,-70"`) + metarCmd.Flags().Var( + &radial, "radial", + `A distance around a radius to search within (dist,centerLat,centerLon) +e.g. "3.5,40,-71"`) + metarCmd.Flags().Float64Var( + &pathDist, "path-dist", 0, + "The distance along the flight path to search METARs for") + metarCmd.Flags().StringSliceVar( + &waypoints, "waypoint", []string{}, + `Multiple waypoint arguments specify the waypoints that make up a flight +path, ordered from origin to destination. They may be specified as ICAO +identifiers e.g. "KBOS", or as lon-lat pairs, e.g. "-71,40".`) + metarCmd.Flags().Float64Var( + &minDist, "min-dist", 0, + `The minimum distance in degrees latitude or longitude between stations +reported. A higher value results in less dense results.`) + + // result count limiting + metarCmd.Flags().StringVar( + &mostRecentByStation, "most-recent-by-station", "constraint", + `Method for fetching most recent for each station ("constraint", +"postfilter", "false")`) + metarCmd.Flags().BoolVarP( + &mostRecent, "one", "1", false, + `Only fetch the single most recently-issued METAR of all the selected +stations`) +} + +var metarCmd = &cobra.Command{ + Use: "metar", + Short: "Fetch METARs", + Long: "Fetch METARs matching the supplied criteria from the TDS", + Run: func(cmd *cobra.Command, args []string) { + reqArgs := []func(*metar.Request){} + + if len(args) > 0 { + reqArgs = append(reqArgs, metar.StationString(strings.Join(args, " "))) + } + + if !since.t.IsZero() || !until.t.IsZero() { + reqArgs = append(reqArgs, metar.TimeRange(since.t, until.t)) + } + + dur, _ := cmd.Flags().GetDuration("past") + if dur > 0 { + reqArgs = append(reqArgs, metar.HoursBeforeNow(dur.Hours())) + } + + if mostRecentByStation != "" { + reqArgs = append(reqArgs, metar.MostRecentForEachStation(mostRecentByStation)) + } + + if mostRecent { + reqArgs = append(reqArgs, metar.MostRecent(true)) + } + + if !rect.IsZero() { + reqArgs = append(reqArgs, metar.LonLatRect(rect.minLat, rect.minLon, rect.maxLat, rect.maxLon)) + } + + if !radial.IsZero() { + reqArgs = append(reqArgs, metar.RadialDistance(radial.radius, radial.lat, radial.lon)) + } + + if pathDist > 0 && len(waypoints) > 0 { + reqArgs = append(reqArgs, metar.FlightPath(pathDist, waypoints...)) + } + + if minDist > 0 { + reqArgs = append(reqArgs, metar.MinDegreeDistance(minDist)) + } + + var mc metar.Client + res, err := mc.GetMETARs(context.Background(), metar.NewRequest(reqArgs...)) + if err != nil { + fmt.Fprintf(os.Stderr, "could not get metar: %v\n", err) + os.Exit(1) + } + if len(res.Errors) > 0 { + for _, err := range res.Errors { + fmt.Fprintf(os.Stderr, "error: %s\n", err) + } + os.Exit(2) + } + for _, m := range res.Data.METARs { + fmt.Printf("%s\n", strings.TrimSpace(m.RawText)) + } + }, +} -- cgit v1.2.3