-
Notifications
You must be signed in to change notification settings - Fork 71
/
openweathermap.go
302 lines (273 loc) · 7.98 KB
/
openweathermap.go
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// Copyright 2022 Brian J. Downs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package openweathermap
import (
"errors"
"net/http"
"strings"
)
var (
errUnitUnavailable = errors.New("unit unavailable")
errLangUnavailable = errors.New("language unavailable")
errInvalidKey = errors.New("invalid api key")
errInvalidOption = errors.New("invalid option")
errInvalidHttpClient = errors.New("invalid http client")
errForecastUnavailable = errors.New("forecast unavailable")
errExcludesUnavailable = errors.New("onecall excludes unavailable")
errCountOfCityIDs = errors.New("count of ids should not be more than 20 per request")
)
// DataUnits represents the character chosen to represent the temperature notation
var DataUnits = map[string]string{"C": "metric", "F": "imperial", "K": "internal"}
var (
baseURL = "https://api.openweathermap.org/data/2.5/weather?%s"
onecallURL = "https://api.openweathermap.org/data/3.0/onecall/%s"
iconURL = "https://openweathermap.org/img/w/%s"
groupURL = "http://api.openweathermap.org/data/2.5/group?%s"
stationURL = "https://api.openweathermap.org/data/2.5/station?id=%d"
forecast5Base = "https://api.openweathermap.org/data/2.5/forecast?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
forecast16Base = "https://api.openweathermap.org/data/2.5/forecast/daily?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d"
historyURL = "https://api.openweathermap.org/data/2.5/history/%s"
pollutionURL = "https://api.openweathermap.org/data/2.5/air_pollution?appid=%s&lat=%s&lon=%s"
uvURL = "https://api.openweathermap.org/data/2.5/"
dataPostURL = "https://openweathermap.org/data/post"
)
// LangCodes holds all supported languages to be used
// inspried and sourced from @bambocher (github.com/bambocher)
var LangCodes = map[string]string{
"AF": "Afrikaans",
"AL": "Albanian",
"AR": "Arabic",
"AZ": "Azerbaijani",
"BG": "Bulgarian",
"CA": "Catalan",
"CZ": "Czech",
"DA": "Danish",
"DE": "German",
"EL": "Greek",
"EN": "English",
"ES": "Spanish",
"EU": "Basque",
"FA": "Persian (Farsi)",
"FI": "Finnish",
"FR": "French",
"GL": "Galician",
"HE": "Hebrew",
"HI": "Hindi",
"HR": "Croatian",
"HU": "Hungarian",
"ID": "Indonesian",
"IT": "Italian",
"JA": "Japanese",
"KR": "Korean",
"LA": "Latvian",
"LT": "Lithuanian",
"MK": "Macedonian",
"NL": "Dutch",
"NO": "Norwegian",
"PL": "Polish",
"PT": "Portuguese",
"PT_BR": "Português Brasil",
"RO": "Romanian",
"RU": "Russian",
"SE": "Swedish",
"SK": "Slovak",
"SL": "Slovenian",
"SP": "Spanish",
"SR": "Serbian",
"SV": "Swedish",
"TH": "Thai",
"TR": "Turkish",
"UA": "Ukrainian",
"UK": "Ukrainian",
"VI": "Vietnamese",
"ZH_CN": "Chinese Simplified",
"ZH_TW": "Chinese Traditional",
"ZU": "Zulu",
}
// Exclude holds all supported excludes option to be used
const (
ExcludeCurrent = "current"
ExcludeMinutely = "minutely"
ExcludeHourly = "hourly"
ExcludeDaily = "daily"
ExcludeAlerts = "alerts"
)
var Excludes []string = []string{
ExcludeCurrent,
ExcludeMinutely,
ExcludeHourly,
ExcludeDaily,
ExcludeAlerts,
}
// Config will hold default settings to be passed into the
// "NewCurrent, NewForecast, etc}" functions.
type Config struct {
Mode string // user choice of JSON or XML
Unit string // measurement for results to be displayed. F, C, or K
Lang string // should reference a key in the LangCodes map
APIKey string // API Key for connecting to the OWM
Username string // Username for posting data
Password string // Pasword for posting data
}
// APIError returned on failed API calls.
type APIError struct {
Message string `json:"message"`
COD string `json:"cod"`
}
// Coordinates struct holds longitude and latitude data in returned
// JSON or as parameter data for requests using longitude and latitude.
type Coordinates struct {
Longitude float64 `json:"lon"`
Latitude float64 `json:"lat"`
}
// Sys struct contains general information about the request
// and the surrounding area for where the request was made.
type Sys struct {
Type int `json:"type"`
ID int `json:"id"`
Message float64 `json:"message"`
Country string `json:"country"`
Sunrise int `json:"sunrise"`
Sunset int `json:"sunset"`
}
// Wind struct contains the speed and degree of the wind.
type Wind struct {
Speed float64 `json:"speed"`
Deg float64 `json:"deg"`
}
// Weather struct holds high-level, basic info on the returned
// data.
type Weather struct {
ID int `json:"id"`
Main string `json:"main"`
Description string `json:"description"`
Icon string `json:"icon"`
}
// Main struct contains the temperates, humidity, pressure for the request.
type Main struct {
Temp float64 `json:"temp"`
TempMin float64 `json:"temp_min"`
TempMax float64 `json:"temp_max"`
FeelsLike float64 `json:"feels_like"`
Pressure float64 `json:"pressure"`
SeaLevel float64 `json:"sea_level"`
GrndLevel float64 `json:"grnd_level"`
Humidity int `json:"humidity"`
}
// Clouds struct holds data regarding cloud cover.
type Clouds struct {
All int `json:"all"`
}
// return key
// }
func setKey(key string) (string, error) {
if err := ValidAPIKey(key); err != nil {
return "", err
}
return key, nil
}
// ValidDataUnit makes sure the string passed in is an accepted
// unit of measure to be used for the return data.
func ValidDataUnit(u string) bool {
for d := range DataUnits {
if u == d {
return true
}
}
return false
}
// ValidLangCode makes sure the string passed in is an
// acceptable lang code.
func ValidLangCode(c string) bool {
for d := range LangCodes {
if c == d {
return true
}
}
return false
}
// ValidDataUnitSymbol makes sure the string passed in is an
// acceptable data unit symbol.
func ValidDataUnitSymbol(u string) bool {
for _, d := range DataUnits {
if u == d {
return true
}
}
return false
}
// ValidExcludes makes sure the string passed in is an
// acceptable excludes options.
func ValidExcludes(e []string) (string, error) {
list := make([]string, 0)
for _, v := range e {
vl := strings.ToLower(v)
notFound := true
for _, d := range Excludes {
if d == vl {
list = append(list, v)
notFound = false
break
}
}
if notFound {
return "", errExcludesUnavailable
}
}
return strings.Join(list, ","), nil
}
// ValidAPIKey makes sure that the key given is a valid one
func ValidAPIKey(key string) error {
if len(key) > 64 {
return errInvalidKey
}
return nil
}
// CheckAPIKeyExists will see if an API key has been set.
func (c *Config) CheckAPIKeyExists() bool { return len(c.APIKey) > 1 }
// Settings holds the client settings
type Settings struct {
client *http.Client
}
// NewSettings returns a new Setting pointer with default http client.
func NewSettings() *Settings {
return &Settings{
client: http.DefaultClient,
}
}
// Optional client settings
type Option func(s *Settings) error
// WithHttpClient sets custom http client when creating a new Client.
func WithHttpClient(c *http.Client) Option {
return func(s *Settings) error {
if c == nil {
return errInvalidHttpClient
}
s.client = c
return nil
}
}
// setOptions sets Optional client settings to the Settings pointer
func setOptions(settings *Settings, options []Option) error {
for _, option := range options {
if option == nil {
return errInvalidOption
}
err := option(settings)
if err != nil {
return err
}
}
return nil
}