1// Copyright 2015 Brian J. Downs 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package openweathermap 16 17import ( 18 "errors" 19 "net/http" 20) 21 22var errUnitUnavailable = errors.New("unit unavailable") 23var errLangUnavailable = errors.New("language unavailable") 24var errInvalidKey = errors.New("invalid api key") 25var errInvalidOption = errors.New("invalid option") 26var errInvalidHttpClient = errors.New("invalid http client") 27var errForecastUnavailable = errors.New("forecast unavailable") 28 29// DataUnits represents the character chosen to represent the temperature notation 30var DataUnits = map[string]string{"C": "metric", "F": "imperial", "K": "internal"} 31var ( 32 baseURL = "http://api.openweathermap.org/data/2.5/weather?%s" 33 iconURL = "http://openweathermap.org/img/w/%s" 34 stationURL = "http://api.openweathermap.org/data/2.5/station?id=%d" 35 forecast5Base = "http://api.openweathermap.org/data/2.5/forecast?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d" 36 forecast16Base = "http://api.openweathermap.org/data/2.5/forecast/daily?appid=%s&%s&mode=json&units=%s&lang=%s&cnt=%d" 37 historyURL = "http://api.openweathermap.org/data/2.5/history/%s" 38 pollutionURL = "http://api.openweathermap.org/pollution/v1/co/" 39 uvURL = "http://api.openweathermap.org/data/2.5/" 40 dataPostURL = "http://openweathermap.org/data/post" 41) 42 43// LangCodes holds all supported languages to be used 44// inspried and sourced from @bambocher (github.com/bambocher) 45var LangCodes = map[string]string{ 46 "EN": "English", 47 "RU": "Russian", 48 "IT": "Italian", 49 "ES": "Spanish", 50 "SP": "Spanish", 51 "UK": "Ukrainian", 52 "UA": "Ukrainian", 53 "DE": "German", 54 "PT": "Portuguese", 55 "RO": "Romanian", 56 "PL": "Polish", 57 "FI": "Finnish", 58 "NL": "Dutch", 59 "FR": "French", 60 "BG": "Bulgarian", 61 "SV": "Swedish", 62 "SE": "Swedish", 63 "TR": "Turkish", 64 "HR": "Croatian", 65 "CA": "Catalan", 66 "ZH_TW": "Chinese Traditional", 67 "ZH": "Chinese Simplified", 68 "ZH_CN": "Chinese Simplified", 69} 70 71// Config will hold default settings to be passed into the 72// "NewCurrent, NewForecast, etc}" functions. 73type Config struct { 74 Mode string // user choice of JSON or XML 75 Unit string // measurement for results to be displayed. F, C, or K 76 Lang string // should reference a key in the LangCodes map 77 APIKey string // API Key for connecting to the OWM 78 Username string // Username for posting data 79 Password string // Pasword for posting data 80} 81 82// APIError returned on failed API calls. 83type APIError struct { 84 Message string `json:"message"` 85 COD string `json:"cod"` 86} 87 88// Coordinates struct holds longitude and latitude data in returned 89// JSON or as parameter data for requests using longitude and latitude. 90type Coordinates struct { 91 Longitude float64 `json:"lon"` 92 Latitude float64 `json:"lat"` 93} 94 95// Sys struct contains general information about the request 96// and the surrounding area for where the request was made. 97type Sys struct { 98 Type int `json:"type"` 99 ID int `json:"id"` 100 Message float64 `json:"message"` 101 Country string `json:"country"` 102 Sunrise int `json:"sunrise"` 103 Sunset int `json:"sunset"` 104} 105 106// Wind struct contains the speed and degree of the wind. 107type Wind struct { 108 Speed float64 `json:"speed"` 109 Deg float64 `json:"deg"` 110} 111 112// Weather struct holds high-level, basic info on the returned 113// data. 114type Weather struct { 115 ID int `json:"id"` 116 Main string `json:"main"` 117 Description string `json:"description"` 118 Icon string `json:"icon"` 119} 120 121// Main struct contains the temperates, humidity, pressure for the request. 122type Main struct { 123 Temp float64 `json:"temp"` 124 TempMin float64 `json:"temp_min"` 125 TempMax float64 `json:"temp_max"` 126 Pressure float64 `json:"pressure"` 127 SeaLevel float64 `json:"sea_level"` 128 GrndLevel float64 `json:"grnd_level"` 129 Humidity int `json:"humidity"` 130} 131 132// Clouds struct holds data regarding cloud cover. 133type Clouds struct { 134 All int `json:"all"` 135} 136 137// return key 138// } 139func setKey(key string) (string, error) { 140 if err := ValidAPIKey(key); err != nil { 141 return "", err 142 } 143 return key, nil 144} 145 146// ValidDataUnit makes sure the string passed in is an accepted 147// unit of measure to be used for the return data. 148func ValidDataUnit(u string) bool { 149 for d := range DataUnits { 150 if u == d { 151 return true 152 } 153 } 154 return false 155} 156 157// ValidLangCode makes sure the string passed in is an 158// acceptable lang code. 159func ValidLangCode(c string) bool { 160 for d := range LangCodes { 161 if c == d { 162 return true 163 } 164 } 165 return false 166} 167 168// ValidDataUnitSymbol makes sure the string passed in is an 169// acceptable data unit symbol. 170func ValidDataUnitSymbol(u string) bool { 171 for _, d := range DataUnits { 172 if u == d { 173 return true 174 } 175 } 176 return false 177} 178 179// ValidAPIKey makes sure that the key given is a valid one 180func ValidAPIKey(key string) error { 181 if len(key) != 32 { 182 return errors.New("invalid key") 183 } 184 return nil 185} 186 187// CheckAPIKeyExists will see if an API key has been set. 188func (c *Config) CheckAPIKeyExists() bool { return len(c.APIKey) > 1 } 189 190// Settings holds the client settings 191type Settings struct { 192 client *http.Client 193} 194 195// NewSettings returns a new Setting pointer with default http client. 196func NewSettings() *Settings { 197 return &Settings{ 198 client: http.DefaultClient, 199 } 200} 201 202// Optional client settings 203type Option func(s *Settings) error 204 205// WithHttpClient sets custom http client when creating a new Client. 206func WithHttpClient(c *http.Client) Option { 207 return func(s *Settings) error { 208 if c == nil { 209 return errInvalidHttpClient 210 } 211 s.client = c 212 return nil 213 } 214} 215 216// setOptions sets Optional client settings to the Settings pointer 217func setOptions(settings *Settings, options []Option) error { 218 for _, option := range options { 219 if option == nil { 220 return errInvalidOption 221 } 222 err := option(settings) 223 if err != nil { 224 return err 225 } 226 } 227 return nil 228} 229