186 lines
3.7 KiB
Go
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)
|
|
}
|