package main import ( "encoding/json" "fmt" "io" "net/http" "time" ) // free api for reverse geocoding based on IP const revGeoURL = "http://ip-api.com/json/" type Location struct { Lat float64 Lon float64 City string State string Country string Zipcode string // NWS specific location data // Station string } func NewLocation() *Location { return &Location{} } func (l *Location) UnmarshalJSON(b []byte) error { var aux struct { Lat float64 `json:"lat,string"` Lon float64 `json:"lon,string"` Address struct { City string `json:"city"` State string `json:"state"` Country string `json:"country"` Zipcode string `json:"postcode"` } `json:"address"` } if err := json.Unmarshal(b, &aux); err != nil { return err } // copy over values l.Lat = aux.Lat l.Lon = aux.Lon l.City = aux.Address.City l.State = aux.Address.State l.Country = aux.Address.Country l.Zipcode = aux.Address.Zipcode return nil } func SearchLocations(query string) ([]Location, error) { // url to perform location queries against //locations := []Location{} queryURL := fmt.Sprintf("https://nominatim.openstreetmap.org/search?q=%s&format=jsonv2&addressdetails=1", query) req, err := http.NewRequest(http.MethodGet, queryURL, nil) if err != nil { return []Location{}, err } req.Header.Set("User-Agent", "weather/0.1 (https://git.keegandeppe.com/kdeppe/weather; contact=19keegandeppe@gmail.com)") client := &http.Client{ Timeout: 15 * time.Second, } resp, err := client.Do(req) if err != nil { return []Location{}, err } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err != nil { return []Location{}, err } var locations []Location if err := json.Unmarshal(b, &locations); err != nil { panic(err) } // l := NewLocation() // l.UnmarshalJSON(b) fmt.Printf("%+v\n", locations) return locations, nil } // 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)", l.City, l.State, l.Zipcode, l.Lat, l.Lon) }