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 15// 16// weather.go 17// 18// This application will go out and get the weather for the given 19// location and display it in the given data units (fahrenheit, 20// celcius, or kelvin). If the string "here" is provided as an 21// argument to the -l flag, the app will try to figure out where 22// it's being executed from based on geolocation from the IP address. 23// 24// Examples: 25// go run weather.go --help 26// go run weather.go -w Philadelphia -u f -l en # fahrenheit, English 27// go run weather.go -w here -u f -l ru # fahrenheit, Russian 28// go run weather.go -w Dublin -u c -l fi # celcius, Finnish 29// go run weather.go -w "Las Vegas" -u k -l es # kelvin, Spanish 30package main 31 32import ( 33 "encoding/json" 34 "flag" 35 owm "github.com/briandowns/openweathermap" // "owm" for easier use 36 "io/ioutil" 37 "log" 38 "net/http" 39 "os" 40 "strings" 41 "text/template" 42) 43 44// URL is a constant that contains where to find the IP locale info 45const URL = "http://ip-api.com/json" 46 47// template used for output 48const weatherTemplate = `Current weather for {{.Name}}: 49 Conditions: {{range .Weather}} {{.Description}} {{end}} 50 Now: {{.Main.Temp}} {{.Unit}} 51 High: {{.Main.TempMax}} {{.Unit}} 52 Low: {{.Main.TempMin}} {{.Unit}} 53` 54 55const forecastTemplate = `Weather Forecast for {{.City.Name}}: 56{{range .List}}Date & Time: {{.DtTxt}} 57Conditions: {{range .Weather}}{{.Main}} {{.Description}}{{end}} 58Temp: {{.Main.Temp}} 59High: {{.Main.TempMax}} 60Low: {{.Main.TempMin}} 61 62{{end}} 63` 64 65// Pointers to hold the contents of the flag args. 66var ( 67 whereFlag = flag.String("w", "", "Location to get weather. If location has a space, wrap the location in double quotes.") 68 unitFlag = flag.String("u", "", "Unit of measure to display temps in") 69 langFlag = flag.String("l", "", "Language to display temps in") 70 whenFlag = flag.String("t", "current", "current | forecast") 71) 72 73// Data will hold the result of the query to get the IP 74// address of the caller. 75type Data struct { 76 Status string `json:"status"` 77 Country string `json:"country"` 78 CountryCode string `json:"countryCode"` 79 Region string `json:"region"` 80 RegionName string `json:"regionName"` 81 City string `json:"city"` 82 Zip string `json:"zip"` 83 Lat float64 `json:"lat"` 84 Lon float64 `json:"lon"` 85 Timezone string `json:"timezone"` 86 ISP string `json:"isp"` 87 ORG string `json:"org"` 88 AS string `json:"as"` 89 Message string `json:"message"` 90 Query string `json:"query"` 91} 92 93// getLocation will get the location details for where this 94// application has been run from. 95func getLocation() (*Data, error) { 96 response, err := http.Get(URL) 97 if err != nil { 98 return nil, err 99 } 100 defer response.Body.Close() 101 102 result, err := ioutil.ReadAll(response.Body) 103 if err != nil { 104 return nil, err 105 } 106 107 r := &Data{} 108 if err = json.Unmarshal(result, &r); err != nil { 109 return nil, err 110 } 111 return r, nil 112} 113 114// getCurrent gets the current weather for the provided 115// location in the units provided. 116func getCurrent(location, units, lang string) (*owm.CurrentWeatherData, error) { 117 w, err := owm.NewCurrent(units, lang, os.Getenv("OWM_API_KEY")) 118 if err != nil { 119 return nil, err 120 } 121 w.CurrentByName(location) 122 return w, nil 123} 124func getForecast5(location, units, lang string) (*owm.Forecast5WeatherData, error) { 125 w, err := owm.NewForecast("5", units, lang, os.Getenv("OWM_API_KEY")) 126 if err != nil { 127 return nil, err 128 } 129 w.DailyByName(location, 5) 130 forecast := w.ForecastWeatherJson.(*owm.Forecast5WeatherData) 131 return forecast, err 132} 133 134func main() { 135 flag.Parse() 136 137 // If there's any funkiness with cli args, tuck and roll... 138 if len(*whereFlag) <= 1 || len(*unitFlag) != 1 || len(*langFlag) != 2 || len(*whenFlag) <= 1 { 139 flag.Usage() 140 os.Exit(1) 141 } 142 143 // Process request for location of "here" 144 if strings.ToLower(*whereFlag) == "here" { 145 loc, err := getLocation() 146 if err != nil { 147 log.Fatalln(err) 148 } 149 w, err := getCurrent(loc.City, *unitFlag, *langFlag) 150 if err != nil { 151 log.Fatalln(err) 152 } 153 tmpl, err := template.New("weather").Parse(weatherTemplate) 154 if err != nil { 155 log.Fatalln(err) 156 } 157 158 // Render the template and display 159 err = tmpl.Execute(os.Stdout, w) 160 if err != nil { 161 log.Fatalln(err) 162 } 163 os.Exit(0) 164 } 165 166 if *whenFlag == "current" { 167 // Process request for the given location 168 w, err := getCurrent(*whereFlag, *unitFlag, *langFlag) 169 if err != nil { 170 log.Fatalln(err) 171 } 172 tmpl, err := template.New("weather").Parse(weatherTemplate) 173 if err != nil { 174 log.Fatalln(err) 175 } 176 // Render the template and display 177 if err := tmpl.Execute(os.Stdout, w); err != nil { 178 log.Fatalln(err) 179 } 180 } else { //forecast 181 w, err := getForecast5(*whereFlag, *unitFlag, *langFlag) 182 if err != nil { 183 log.Fatalln(err) 184 } 185 tmpl, err := template.New("forecast").Parse(forecastTemplate) 186 if err != nil { 187 log.Fatalln(err) 188 } 189 // Render the template and display 190 if err := tmpl.Execute(os.Stdout, w); err != nil { 191 log.Fatalln(err) 192 } 193 } 194 195 os.Exit(0) 196} 197