Files
heloha/internal/radar/fetch.go
2026-04-25 20:31:55 -05:00

52 lines
1.5 KiB
Go

package radar
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
)
// nwsRadarBase is the NWS public product distribution server.
// No credentials required; sn.last always holds the most recent scan.
const nwsRadarBase = "https://tgftp.nws.noaa.gov/SL.us008001/DF.of/DC.radar"
// Fetcher downloads NEXRAD Level 3 products from the NWS public server.
type Fetcher struct {
client *http.Client
}
func NewFetcher(_ context.Context) (*Fetcher, error) {
return &Fetcher{client: &http.Client{Timeout: 30 * time.Second}}, nil
}
// FetchLatest downloads the latest Level 3 base reflectivity (0.5°) for site.
func (f *Fetcher) FetchLatest(ctx context.Context, site string) ([]byte, error) {
return f.fetchDS(ctx, site, "p94r0")
}
// FetchVelocity downloads the latest Level 3 base velocity (0.5°) for site.
func (f *Fetcher) FetchVelocity(ctx context.Context, site string) ([]byte, error) {
return f.fetchDS(ctx, site, "p99r0")
}
func (f *Fetcher) fetchDS(ctx context.Context, site, ds string) ([]byte, error) {
url := fmt.Sprintf("%s/DS.%s/SI.%s/sn.last", nwsRadarBase, ds, strings.ToLower(site))
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
resp, err := f.client.Do(req)
if err != nil {
return nil, fmt.Errorf("NWS fetch: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("NWS returned %s for %s", resp.Status, url)
}
return io.ReadAll(resp.Body)
}