2023-11-10 23:22:46 +00:00
|
|
|
package nws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"code.jhot.me/jhot/hats/internal/util"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
|
|
"github.com/samber/lo"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
NWS_BASE_URL = "https://api.weather.gov"
|
|
|
|
)
|
|
|
|
|
|
|
|
type NwsClient struct {
|
|
|
|
restClient *resty.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func New() *NwsClient {
|
|
|
|
return &NwsClient{
|
|
|
|
restClient: resty.New().
|
|
|
|
SetBaseURL(NWS_BASE_URL).
|
|
|
|
SetHeader("Accept", "application/geo+json").
|
|
|
|
SetHeader("User-Agent", util.GetEnvWithDefault("NWS_USER_AGENT", "(HATS-client, hats@jhot.me)")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *NwsClient) CoordinatesToPoint(lat float64, lon float64) (Point, error) {
|
|
|
|
var p Point
|
|
|
|
_, err := c.restClient.R().SetResult(&p).Get(fmt.Sprintf("points/%f,%f", lat, lon))
|
2024-01-02 18:55:08 +00:00
|
|
|
p.Lat = lat
|
|
|
|
p.Lon = lon
|
2023-11-10 23:22:46 +00:00
|
|
|
return p, err
|
|
|
|
}
|
|
|
|
|
2024-01-02 18:55:08 +00:00
|
|
|
func (p Point) GetWebURL() string {
|
|
|
|
return fmt.Sprintf("https://forecast.weather.gov/MapClick.php?lat=%f&lon=%f", p.Lat, p.Lon)
|
|
|
|
}
|
|
|
|
|
2023-11-10 23:22:46 +00:00
|
|
|
func (c *NwsClient) GetHourlyForecast(p Point) (HourlyProps, error) {
|
|
|
|
var data struct {
|
|
|
|
Properties HourlyProps `json:"properties"`
|
|
|
|
}
|
|
|
|
_, err := util.CheckSuccess(c.restClient.R().SetResult(&data).
|
|
|
|
Get(removeBase(p.Properties.ForecastHourly)))
|
|
|
|
if err != nil {
|
|
|
|
return HourlyProps{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.Properties, nil
|
|
|
|
}
|
|
|
|
|
2024-01-02 18:55:08 +00:00
|
|
|
func (c *NwsClient) GetDailyForecast(p Point) (DailyProps, error) {
|
|
|
|
var data struct {
|
|
|
|
Properties DailyProps `json:"properties"`
|
|
|
|
}
|
|
|
|
_, err := util.CheckSuccess(c.restClient.R().SetResult(&data).
|
|
|
|
Get(removeBase(p.Properties.Forecast)))
|
|
|
|
if err != nil {
|
|
|
|
return DailyProps{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.Properties, nil
|
|
|
|
}
|
|
|
|
|
2023-11-10 23:22:46 +00:00
|
|
|
func (p HourlyProps) GetHighLow(hours int) (high int, low int) {
|
|
|
|
if hours > len(p.Periods) {
|
|
|
|
hours = len(p.Periods)
|
|
|
|
}
|
|
|
|
|
|
|
|
temps := lo.Map(p.Periods[0:hours-1], func(p HourlyPeriod, _ int) int {
|
|
|
|
return p.Temperature
|
|
|
|
})
|
|
|
|
|
|
|
|
return lo.Max(temps), lo.Min(temps)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *NwsClient) GetStations(p Point) ([]string, error) {
|
|
|
|
var data struct {
|
|
|
|
ObservationStations []string `json:"observationStations"`
|
|
|
|
}
|
|
|
|
_, err := util.CheckSuccess(c.restClient.R().SetResult(&data).
|
|
|
|
Get(removeBase(p.Properties.ObservationStations)))
|
|
|
|
if err != nil {
|
|
|
|
return []string{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.ObservationStations, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *NwsClient) GetLatestObservations(p Point) (Observations, error) {
|
|
|
|
stations, err := c.GetStations(p)
|
|
|
|
if err != nil {
|
|
|
|
return Observations{}, fmt.Errorf("%w: error getting stations", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var data struct {
|
|
|
|
Properties Observations `json:"properties"`
|
|
|
|
}
|
|
|
|
_, err = util.CheckSuccess(c.restClient.R().SetResult(&data).
|
|
|
|
Get(removeBase(stations[0] + "/observations/latest")))
|
|
|
|
if err != nil {
|
|
|
|
return Observations{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.Properties, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeBase(longUrl string) string {
|
|
|
|
return strings.TrimPrefix(longUrl, NWS_BASE_URL+"/")
|
|
|
|
}
|