weather/location.go
2025-12-24 13:31:09 -05:00

186 lines
3.7 KiB
Go

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)
}