1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
// Copyright © 2022 siddharth <s@ricketyspace.net>
// SPDX-License-Identifier: ISC
// Functions for accessing the National Weather Service API.
package nws
import (
"encoding/json"
"fmt"
"io"
"ricketyspace.net/peach/client"
)
type NWSPointProperties struct {
GridId string
GridX int
GridY int
Forecast string
ForecastHourly string
}
type NWSPoint struct {
Properties NWSPointProperties
}
type NWSForecastPeriod struct {
Number int
Name string
StartTime string
EndTime string
IsDayTime bool
Temperature int
TemperatureUnit string
TemperatureTrend string
WindSpeed string
WindDirection string
ShortForecast string
DetailedForecast string
}
type NWSForecastProperties struct {
Periods []NWSForecastPeriod
}
type NWSForecast struct {
Properties NWSForecastProperties
}
type NWSError struct {
Title string
Type string
Status int
Detail string
}
func (e NWSError) Error() string {
return fmt.Sprintf("%d: %s: %s", e.Status, e.Type, e.Detail)
}
// NWS `/points` endpoint.
func Points(lat, lng float32) (*NWSPoint, error) {
url := fmt.Sprintf("https://api.weather.gov/points/%.4f,%.4f", lat, lng)
resp, err := client.Get(url)
if err != nil {
return nil, fmt.Errorf("points: http get: %v", err)
}
// Parse response body.
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("points: body: %v", err)
}
// Check if the request failed.
if resp.StatusCode != 200 {
perr := new(NWSError)
err := json.Unmarshal(body, perr)
if err != nil {
return nil, fmt.Errorf("points: json: %v", err)
}
return nil, fmt.Errorf("points: %v", perr)
}
// Unmarshal.
point := new(NWSPoint)
err = json.Unmarshal(body, point)
if err != nil {
return nil, fmt.Errorf("points: decode: %v", err)
}
if point.Properties.Forecast == "" {
return nil, fmt.Errorf("points: forecast empty")
}
if point.Properties.ForecastHourly == "" {
return nil, fmt.Errorf("points: forecasthourly empty")
}
return point, nil
}
// NWS forecast endpoint.
func Forecast(point *NWSPoint) (*NWSForecast, error) {
if point == nil {
return nil, fmt.Errorf("forecast: point nil")
}
if len(point.Properties.Forecast) == 0 {
return nil, fmt.Errorf("forecast: link empty")
}
// Get the forecast
resp, err := client.Get(point.Properties.Forecast)
if err != nil {
return nil, fmt.Errorf("forecast: get: %v", err)
}
// Parse response body.
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("forecast: body: %v", err)
}
// Check if the request failed.
if resp.StatusCode != 200 {
perr := new(NWSError)
err := json.Unmarshal(body, perr)
if err != nil {
return nil, fmt.Errorf("forecast: json: %v", err)
}
return nil, fmt.Errorf("forecast: %v", perr)
}
// Unmarshal.
forecast := new(NWSForecast)
err = json.Unmarshal(body, forecast)
if err != nil {
return nil, fmt.Errorf("forecast: decode: %v", err)
}
if len(forecast.Properties.Periods) == 0 {
return nil, fmt.Errorf("forecast: periods empty")
}
return forecast, nil
}
|