migrating to go
This commit is contained in:
		
							parent
							
								
									d27117b8bc
								
							
						
					
					
						commit
						7d14f3a9df
					
				
							
								
								
									
										10
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					module weather
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.22.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/tidwall/gjson v1.17.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/tidwall/match v1.1.1 // indirect
 | 
				
			||||||
 | 
						github.com/tidwall/pretty v1.2.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
 | 
				
			||||||
 | 
					github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 | 
				
			||||||
 | 
					github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 | 
				
			||||||
 | 
					github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
 | 
				
			||||||
 | 
					github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 | 
				
			||||||
 | 
					github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 | 
				
			||||||
							
								
								
									
										187
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/tidwall/gjson"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// free api for reverse geocoding based on IP
 | 
				
			||||||
 | 
					const revGeoURL = "http://ip-api.com/json/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						w := NewWeather()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := w.getLatest(); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println(w)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Weather struct {
 | 
				
			||||||
 | 
						Temperature float64
 | 
				
			||||||
 | 
						Humidity    float64
 | 
				
			||||||
 | 
						Conditions  string
 | 
				
			||||||
 | 
						Icon        string
 | 
				
			||||||
 | 
						*Location
 | 
				
			||||||
 | 
						Static bool //
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewWeather() *Weather {
 | 
				
			||||||
 | 
						l := NewLocation()
 | 
				
			||||||
 | 
						// try to set location
 | 
				
			||||||
 | 
						if err := l.getCoords(); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := l.getStation(); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &Weather{
 | 
				
			||||||
 | 
							Location: l,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w *Weather) getLatest() error {
 | 
				
			||||||
 | 
						url := fmt.Sprintf("https://api.weather.gov/stations/%s/observations/latest", w.Location.Station)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := http.Get(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						body, err := io.ReadAll(res.Body)
 | 
				
			||||||
 | 
						res.Body.Close()
 | 
				
			||||||
 | 
						if res.StatusCode > 299 {
 | 
				
			||||||
 | 
							errMsg := fmt.Sprintf("Request failed with status code: %d\n", res.StatusCode)
 | 
				
			||||||
 | 
							return errors.New(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						temp := gjson.Get(string(body), "properties.temperature.value")
 | 
				
			||||||
 | 
						humidity := gjson.Get(string(body), "properties.relativeHumidity.value")
 | 
				
			||||||
 | 
						cond := gjson.Get(string(body), "properties.textDescription")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// convert to Farenheit
 | 
				
			||||||
 | 
						w.Temperature = temp.Float()*(9.0/5) + 32
 | 
				
			||||||
 | 
						w.Humidity = humidity.Float()
 | 
				
			||||||
 | 
						w.Conditions = cond.String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w *Weather) String() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s %.1f deg %.1f%% RH in %s, %s", w.Conditions, w.Temperature, w.Humidity, w.Location.City, w.Location.State)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Location struct {
 | 
				
			||||||
 | 
						Lat     float32 `json:"lat"`
 | 
				
			||||||
 | 
						Lon     float32 `json:"lon"`
 | 
				
			||||||
 | 
						City    string  `json:"city"`
 | 
				
			||||||
 | 
						State   string  `json:"region"`
 | 
				
			||||||
 | 
						Zipcode string  `json:"zip"`
 | 
				
			||||||
 | 
						Found   bool
 | 
				
			||||||
 | 
						// NWS specific location data
 | 
				
			||||||
 | 
						Station string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewLocation() *Location {
 | 
				
			||||||
 | 
						return &Location{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *Location) getCoords() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := http.Get(revGeoURL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						body, err := io.ReadAll(res.Body)
 | 
				
			||||||
 | 
						res.Body.Close()
 | 
				
			||||||
 | 
						if res.StatusCode > 299 {
 | 
				
			||||||
 | 
							errMsg := fmt.Sprintf("Request failed with status code: %d\n", res.StatusCode)
 | 
				
			||||||
 | 
							return errors.New(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// unmarshall response into struct
 | 
				
			||||||
 | 
						if err := json.Unmarshal(body, l); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.Found = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l *Location) getStation() error {
 | 
				
			||||||
 | 
						// generate url based on coords
 | 
				
			||||||
 | 
						url := fmt.Sprintf("https://api.weather.gov/points/%f,%f", l.Lat, l.Lon)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := http.Get(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						body, err := io.ReadAll(res.Body)
 | 
				
			||||||
 | 
						res.Body.Close()
 | 
				
			||||||
 | 
						if res.StatusCode > 299 {
 | 
				
			||||||
 | 
							errMsg := fmt.Sprintf("Request failed with status code: %d\n", res.StatusCode)
 | 
				
			||||||
 | 
							return errors.New(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// send another request to station URL to find closest
 | 
				
			||||||
 | 
						stationURL := gjson.Get(string(body), "properties.observationStations")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err = http.Get(stationURL.String())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						body, err = io.ReadAll(res.Body)
 | 
				
			||||||
 | 
						res.Body.Close()
 | 
				
			||||||
 | 
						if res.StatusCode > 299 {
 | 
				
			||||||
 | 
							errMsg := fmt.Sprintf("Request failed with status code: %d\n", res.StatusCode)
 | 
				
			||||||
 | 
							return errors.New(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						station := gjson.Get(string(body), "features.0.properties.stationIdentifier")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if station.String() == "" {
 | 
				
			||||||
 | 
							return errors.New("Station not found")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						l.Station = station.String()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// for debugging
 | 
				
			||||||
 | 
					func (l *Location) String() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s, %s %s (%f, %f) station: %s", l.City, l.State, l.Zipcode, l.Lat, l.Lon, l.Station)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user