Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 333 lines (302 sloc) 9.149 kB
6e041b8 @sramsay Added license header.
authored
1 /*
2 * wu - a small, fast command-line application for retrieving weather
3 * data from Weather Underground
4 *
9c44671 @sramsay Added license headers to all files.
authored
5 * Main and associated functions.
6 *
6e041b8 @sramsay Added license header.
authored
7 * Written and maintained by Stephen Ramsay <sramsay.unl@gmail.com>
8 * and Anthony Starks.
9 *
82f90af @sramsay ...
authored
10 * Last Modified: Sun Sep 01 16:38:59 CDT 2013
6e041b8 @sramsay Added license header.
authored
11 *
590bb97 @sramsay Update to Copyright date range.
authored
12 * Copyright © 2010-2013 by Stephen Ramsay and Anthony Starks.
6e041b8 @sramsay Added license header.
authored
13 *
14 * wu is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3, or (at your option)
17 * any later version.
18 *
19 * wu is distributed in the hope that it will be useful, but WITHOUT
20 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
22 * License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with wu; see the file COPYING. If not see
26 * <http://www.gnu.org/licenses/>.
fbd74ed @sramsay Working on -yesterday (still filling in values).
authored
27 */
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
28
29 package main
30
31 import (
3bed8a0 @sramsay Reformatting.
authored
32 "encoding/json"
33 "flag"
34 "fmt"
35 "io/ioutil"
36 "net/http"
37 "os"
38 "regexp"
39 "strings"
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
40 )
41
42 type Config struct {
3bed8a0 @sramsay Reformatting.
authored
43 Key string
44 Station string
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
45 }
46
47 var (
3bed8a0 @sramsay Reformatting.
authored
48 help bool
49 version bool
50 doall bool
51 doalmanac bool
52 doalerts bool
53 doconditions bool
54 dolookup bool
55 doforecast bool
5d9fcda @sramsay Reformatting
authored
56 doforecast10 bool
3bed8a0 @sramsay Reformatting.
authored
57 doastro bool
58 doyesterday bool
fe3dc6e @sramsay Fixed bug #27.
authored
59 dotides bool
3bed8a0 @sramsay Reformatting.
authored
60 dohistory string
61 doplanner string
62 date string
63 conf Config
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
64 )
65
243e322 @sramsay Finished tidal data (without trapping for non-coastal areas).
authored
66 // Struct common to several data streams
67 type Date struct {
3bed8a0 @sramsay Reformatting.
authored
68 Pretty string
69 Hour string
70 Min string
71 Mon string
72 Mday string
73 Year string
243e322 @sramsay Finished tidal data (without trapping for non-coastal areas).
authored
74 }
75
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
76 const defaultStation = "KLNK"
77
78 // GetVersion returns the version of the package
79 func GetVersion() string {
61d6835 @sramsay Version bump.
authored
80 return "3.9.7"
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
81 }
82
83 // GetConf returns the API key and weather station from
84 // the configuration file at $HOME/.condrc
85 func ReadConf() {
86
c3688d4 @sramsay Minor refactoring to bring code in line with Go idioms.
authored
87 if b, err := ioutil.ReadFile(os.Getenv("HOME") + "/.condrc"); err == nil {
3bed8a0 @sramsay Reformatting.
authored
88 jsonErr := json.Unmarshal(b, &conf)
89 CheckError(jsonErr)
90 } else {
91 fmt.Println("You must create a .condrc file in $HOME.")
92 os.Exit(0)
93 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
94 }
95
96 // Options handles commandline options and returns a
97 // possibly updated weather station string
98 func Options() string {
99
3bed8a0 @sramsay Reformatting.
authored
100 var station, sconf string
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
101
3bed8a0 @sramsay Reformatting.
authored
102 if conf.Station == "" {
103 sconf = defaultStation
104 } else {
105 sconf = conf.Station
106 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
107
3bed8a0 @sramsay Reformatting.
authored
108 flag.BoolVar(&doconditions, "conditions", false, "Reports the current weather conditions")
109 flag.BoolVar(&doalerts, "alerts", false, "Reports any active weather alerts")
110 flag.BoolVar(&dolookup, "lookup", false, "Lookup the codes for the weather stations in a particular area")
111 flag.BoolVar(&doastro, "astro", false, "Reports sunrise, sunset, and lunar phase")
112 flag.BoolVar(&doforecast, "forecast", false, "Reports the current (3-day) forecast")
75fe61d @sramsay Changed the (deprecated) forecast7 to forecast10.
authored
113 flag.BoolVar(&doforecast10, "forecast10", false, "Reports the current (7-day) forecast")
3bed8a0 @sramsay Reformatting.
authored
114 flag.BoolVar(&doalmanac, "almanac", false, "Reports average high, low and record temperatures")
115 flag.BoolVar(&doyesterday, "yesterday", false, "Reports yesterday's weather data")
116 flag.StringVar(&dohistory, "history", "", "Reports historical data for a particular day --history=\"YYYYMMDD\"")
117 flag.StringVar(&doplanner, "planner", "", "Reports historical data for a particular date range (30-day max) --planner=\"MMDDMMDD\"")
fe3dc6e @sramsay Fixed bug #27.
authored
118 flag.BoolVar(&dotides, "tides", false, "Reports tidal data (if available")
cbdc16f @sramsay Expanded -h and -V switches.
authored
119 flag.BoolVar(&help, "help", false, "Print this message")
120 flag.BoolVar(&version, "version", false, "Print the version number")
3bed8a0 @sramsay Reformatting.
authored
121 flag.BoolVar(&doall, "all", false, "Show all weather data")
122 flag.StringVar(&station, "s", sconf,
123 "Weather station: \"city, state-abbreviation\", (US or Canadian) zipcode, 3- or 4-letter airport code, or LAT,LONG")
124 flag.Parse()
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
125
3bed8a0 @sramsay Reformatting.
authored
126 // Check for correct usage of wu -lookup
127 if dolookup {
128 if len(os.Args) == 3 {
129 station = os.Args[len(os.Args)-1]
130 } else {
131 fmt.Println("Usage: wu -lookup [station] where station is a \"city, state-abbreviation\", (US or Canadian) zipcode, 3- or 4-letter airport code, or LAT,LONG")
132 os.Exit(0)
133 }
134 }
15c3e91 @sramsay Fixed syntax of -lookup switch, so that stations can be looked up
authored
135
3bed8a0 @sramsay Reformatting.
authored
136 if help {
137 flag.PrintDefaults()
138 os.Exit(0)
139 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
140
3bed8a0 @sramsay Reformatting.
authored
141 if version {
142 fmt.Println("Wu " + GetVersion())
db3888f @sramsay Copyright update.
authored
143 fmt.Println("Copyright 2010-2014 by Stephen Ramsay and")
3bed8a0 @sramsay Reformatting.
authored
144 fmt.Println("Anthony Starks. Data courtesy of Weather")
145 fmt.Println("Underground, Inc. is subject to Weather")
146 fmt.Println("Underground Data Feed Terms of Service.")
147 fmt.Println("The program itself is free software, and")
148 fmt.Println("you are welcome to redistribute it under")
149 fmt.Println("certain conditions. See LICENSE for details.")
150 os.Exit(0)
151 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
152
3bed8a0 @sramsay Reformatting.
authored
153 // Trap for city-state combinations (e.g. "San Francisco, CA") and
154 // make them URL-friendly (e.g. "CA/SanFranciso")
155 cityStatePattern := regexp.MustCompile("([A-Za-z ]+), ([A-Za-z ]+)")
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
156
c3688d4 @sramsay Minor refactoring to bring code in line with Go idioms.
authored
157 if cityState := cityStatePattern.FindStringSubmatch(station); cityState != nil {
3bed8a0 @sramsay Reformatting.
authored
158 station = cityState[2] + "/" + cityState[1]
159 station = strings.Replace(station, " ", "_", -1)
160 }
161 return station
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
162 }
163
164 // BuildURL returns the URL required by the Weather Underground API
165 // from the query type, station id, and API key
166 func BuildURL(infoType string, stationId string) string {
167
3bed8a0 @sramsay Reformatting.
authored
168 const URLstem = "http://api.wunderground.com/api/"
169 const query = "/q/"
170 const format = ".json"
171 if dohistory != "" {
172 date = dohistory
173 } else if doplanner != "" {
174 date = doplanner
175 }
c3688d4 @sramsay Minor refactoring to bring code in line with Go idioms.
authored
176
177 var URL string
178
3bed8a0 @sramsay Reformatting.
authored
179 if date != "" {
180 URL = URLstem + conf.Key + "/" + infoType + "_" + date + query + stationId + format
181 } else {
182 URL = URLstem + conf.Key + "/" + infoType + "_" + date + query + stationId + format
183 }
7f3fa1d @sramsay Added --history=YYYYMMDD flag.
authored
184
d2e1a87 @sramsay Version bump.
authored
185 // fmt.Println(URL) //DEBUG
80a2cc3 @sramsay Added URL debug line.
authored
186
3bed8a0 @sramsay Reformatting.
authored
187 return URL
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
188 }
189
190 // Fetch does URL processing
a385f01 @sramsay Reorganizing files.
authored
191 func Fetch(url string) ([]byte, error) {
3bed8a0 @sramsay Reformatting.
authored
192 res, err := http.Get(url)
193 CheckError(err)
194 if res.StatusCode != 200 {
195 fmt.Fprintf(os.Stderr, "Bad HTTP Status: %d\n", res.StatusCode)
196 return nil, err
197 }
198 b, err := ioutil.ReadAll(res.Body)
199 res.Body.Close()
200 return b, err
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
201 }
202
203 // CheckError exits on error with a message
a385f01 @sramsay Reorganizing files.
authored
204 func CheckError(err error) {
3bed8a0 @sramsay Reformatting.
authored
205 if err != nil {
206 fmt.Fprintf(os.Stderr, "Fatal error\n%v\n", err)
207 os.Exit(1)
208 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
209 }
210
211 func init() {
3bed8a0 @sramsay Reformatting.
authored
212 ReadConf()
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
213 }
214
215 // weather prints various weather information for a specified station
216 func weather(operation string, station string) {
3bed8a0 @sramsay Reformatting.
authored
217 url := BuildURL(operation, station)
218 b, err := Fetch(url)
219 CheckError(err)
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
220
3bed8a0 @sramsay Reformatting.
authored
221 switch operation {
222 case "almanac":
223 var obs AlmanacConditions
224 jsonErr := json.Unmarshal(b, &obs)
225 CheckError(jsonErr)
226 PrintAlmanac(&obs, station)
227 case "astronomy":
228 var obs AstroConditions
229 jsonErr := json.Unmarshal(b, &obs)
230 CheckError(jsonErr)
231 PrintAstro(&obs, station)
232 case "alerts":
233 var obs AlertConditions
234 jsonErr := json.Unmarshal(b, &obs)
235 CheckError(jsonErr)
236 PrintAlerts(&obs, station)
237 case "conditions":
238 var obs Conditions
239 jsonErr := json.Unmarshal(b, &obs)
240 CheckError(jsonErr)
241 PrintConditions(&obs)
242 case "forecast":
243 var obs ForecastConditions
244 jsonErr := json.Unmarshal(b, &obs)
245 CheckError(jsonErr)
246 PrintForecast(&obs, station)
75fe61d @sramsay Changed the (deprecated) forecast7 to forecast10.
authored
247 case "forecast10day":
3bed8a0 @sramsay Reformatting.
authored
248 var obs ForecastConditions
249 jsonErr := json.Unmarshal(b, &obs)
250 CheckError(jsonErr)
75fe61d @sramsay Changed the (deprecated) forecast7 to forecast10.
authored
251 PrintForecast10(&obs, station)
3bed8a0 @sramsay Reformatting.
authored
252 case "yesterday":
253 var obs HistoryConditions
254 jsonErr := json.Unmarshal(b, &obs)
255 CheckError(jsonErr)
256 PrintHistory(&obs, station)
257 case "history":
258 var obs HistoryConditions
259 jsonErr := json.Unmarshal(b, &obs)
260 CheckError(jsonErr)
261 PrintHistory(&obs, station)
262 case "planner":
263 var obs PlannerConditions
264 jsonErr := json.Unmarshal(b, &obs)
265 CheckError(jsonErr)
266 PrintPlanner(&obs, station)
267 case "tide":
268 var obs TideConditions
269 jsonErr := json.Unmarshal(b, &obs)
270 CheckError(jsonErr)
fe3dc6e @sramsay Fixed bug #27.
authored
271 PrintTides(&obs, station)
3bed8a0 @sramsay Reformatting.
authored
272 case "geolookup":
273 var l Lookup
274 jsonErr := json.Unmarshal(b, &l)
275 CheckError(jsonErr)
276 PrintLookup(&l)
277 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
278 }
279
280 func main() {
3bed8a0 @sramsay Reformatting.
authored
281 stationId := Options()
282 if doall {
283 weather("conditions", stationId)
284 weather("forecast", stationId)
75fe61d @sramsay Changed the (deprecated) forecast7 to forecast10.
authored
285 weather("forecast10day", stationId)
3bed8a0 @sramsay Reformatting.
authored
286 weather("alerts", stationId)
287 weather("almanac", stationId)
288 weather("history", stationId)
289 weather("planner", stationId)
290 weather("yesterday", stationId)
291 weather("astronomy", stationId)
292 weather("tide", stationId)
293 weather("geolookup", stationId)
294 os.Exit(0)
295 }
296 if doalerts {
297 weather("alerts", stationId)
298 }
299 if doalmanac {
300 weather("almanac", stationId)
301 }
302 if doastro {
303 weather("astronomy", stationId)
304 }
305 if doconditions {
306 weather("conditions", stationId)
307 }
308 if doforecast {
309 weather("forecast", stationId)
310 }
75fe61d @sramsay Changed the (deprecated) forecast7 to forecast10.
authored
311 if doforecast10 {
312 weather("forecast10day", stationId)
3bed8a0 @sramsay Reformatting.
authored
313 }
314 if dohistory != "" {
315 weather("history", stationId)
316 }
317 if doyesterday {
318 weather("yesterday", stationId)
319 }
320 if doplanner != "" {
321 weather("planner", stationId)
322 }
fe3dc6e @sramsay Fixed bug #27.
authored
323 if dotides {
3bed8a0 @sramsay Reformatting.
authored
324 weather("tide", stationId)
325 }
326 if dolookup {
327 weather("geolookup", stationId)
328 }
329 if flag.NFlag() == 0 {
330 weather("conditions", stationId)
331 }
5e86bf6 @ajstarks added unified command, wu
ajstarks authored
332 }
Something went wrong with that request. Please try again.