package weather import ( "context" "encoding/json" _ "fmt" "github.com/EricNeid/go-openweather" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/models" "github.com/shawntoffel/go-pirateweather" _ "github.com/shawntoffel/go-pirateweather" "jubilee-server/structs" "time" "log" "strconv" ) const openWeatherAPIKey = "936a0ed9eb23b95cf08fc9f693c24264" const pirateweatherAPIKey = "AdNZIbMMyb3QtqQhN4WA27EPJR7V339m1Uxvg9yl" func GetOpenWeather(app *pocketbase.PocketBase, latlong structs.LatLong) string { log.Println("$$:GetOpenWeather", latlong) log.Println("-- lat", latlong.Lat) log.Println("-- long", latlong.Long) formattedWeather := &structs.FormattedWeather{} // a 1km square latlow := latlong.Lat - 0.005 lathigh := latlong.Lat + 0.005 longlow := latlong.Long - 0.005 longhigh := latlong.Long + 0.005 // an hour ago now := time.Now() then := now.Add(-1 * time.Hour).Unix() log.Printf("-- Between %+v,%+v to %+v,%+v\n", latlow, longlow, lathigh, longhigh) dberr := app.Dao().DB(). Select("*").From("weather"). Where(dbx.NewExp("lat >= {:latlow} and lat <= {:lathigh}", dbx.Params{"latlow": latlow, "lathigh": lathigh})). AndWhere(dbx.NewExp("long >= {:longlow} and long <= {:longhigh}", dbx.Params{"longlow": longlow, "longhigh": longhigh})). AndWhere(dbx.NewExp("type = {:type}", dbx.Params{"type": "openweather"})). AndWhere(dbx.NewExp("ts >= {:then}", dbx.Params{"then": then})).One(&formattedWeather) if dberr == nil { // handle error log.Printf("-- Cache hit OpenWeather %+v,%+v\n", formattedWeather.Lat, formattedWeather.Long) return formattedWeather.Data } data := GetRemoteOpenWeather(latlong) formattedWeather.Lat = latlong.Lat formattedWeather.Long = latlong.Long formattedWeather.Data = data formattedWeather.Ts = now.Unix() formattedWeather.Type = "openweather" go func() { err := saveWeatherRecord(app, *formattedWeather) if err != nil { log.Println(err) } }() return data } func GetRemoteOpenWeather(ll structs.LatLong) string { log.Println("getOpenWeather", ll.Lat, ll.Long) sLat := strconv.FormatFloat(ll.Lat, 'f', -1, 64) sLong := strconv.FormatFloat(ll.Long, 'f', -1, 64) q := openweather.NewQueryForLocation(openWeatherAPIKey, sLat, sLong) resp, _ := q.DailyForecast5() // log.Printf("%+v\n", resp) jsonStr, _ := json.Marshal(resp) // log.Println(string(jsonStr)) return string(jsonStr) } func GetPirateForecast(app *pocketbase.PocketBase, latlong structs.LatLong) string { log.Println("$$:GetPirateForecast", latlong) log.Println("-- lat", latlong.Lat) log.Println("-- long", latlong.Long) formattedWeather := &structs.FormattedWeather{} // a 1km square latlow := latlong.Lat - 0.005 lathigh := latlong.Lat + 0.005 longlow := latlong.Long - 0.005 longhigh := latlong.Long + 0.005 // an hour ago now := time.Now() then := now.Add(-1 * time.Hour).Unix() log.Printf("-- Between %+v,%+v to %+v,%+v\n", latlow, longlow, lathigh, longhigh) dberr := app.Dao().DB(). Select("*").From("weather"). Where(dbx.NewExp("lat >= {:latlow} and lat <= {:lathigh}", dbx.Params{"latlow": latlow, "lathigh": lathigh})). AndWhere(dbx.NewExp("long >= {:longlow} and long <= {:longhigh}", dbx.Params{"longlow": longlow, "longhigh": longhigh})). AndWhere(dbx.NewExp("type = {:type}", dbx.Params{"type": "pirateForecast"})). AndWhere(dbx.NewExp("ts >= {:then}", dbx.Params{"then": then})).One(&formattedWeather) if dberr == nil { // handle error log.Printf("-- Cache hit weather %+v,%+v\n", formattedWeather.Lat, formattedWeather.Long) return formattedWeather.Data } data := GetRemotePirateForecast(latlong) formattedWeather.Lat = latlong.Lat formattedWeather.Long = latlong.Long formattedWeather.Data = data formattedWeather.Ts = now.Unix() formattedWeather.Type = "pirateForecast" go func() { err := saveWeatherRecord(app, *formattedWeather) if err != nil { log.Println(err) } }() return data } func GetRemotePirateForecast(ll structs.LatLong) string { log.Println("GetRemotePirateForecast", ll.Lat, ll.Long) client := pirateweather.Client{} request := pirateweather.ForecastRequest{ Latitude: ll.Lat, Longitude: ll.Long, Options: pirateweather.ForecastRequestOptions{ // Return data in SI units. Units: "uk", Extend: "Extend", }, } ctx := context.Background() response, err := client.Forecast(ctx, pirateweatherAPIKey, request) if err == nil { log.Println("Error:", err) } output := new(structs.ForecastRecord) today := response.Daily.Data[0] output.Currently.Summary = response.Currently.Summary output.Currently.Temperature = response.Currently.Temperature output.Currently.TempMax = today.TemperatureMax output.Currently.TempMin = today.TemperatureMin output.Currently.Icon = today.Icon output.Details.Summary = today.Summary output.Details.Humidity = today.Humidity output.Details.Moonphase = today.MoonPhase output.Details.Pressure = today.Pressure output.Details.SunriseTime = today.SunriseTime output.Details.SunsetTime = today.SunsetTime output.Details.UvIndex = today.UvIndex output.Details.Visibility = today.Visibility output.Details.WindSpeed = today.WindSpeed var daily []structs.ForecastDay var hourly []structs.ForecastHour for _, v := range response.Daily.Data { n := &structs.ForecastDay{ Time: v.Time, Icon: v.Icon, TempHigh: v.TemperatureMax, TempLow: v.TemperatureMin, } daily = append(daily, *n) } for _, v := range response.Hourly.Data { n := &structs.ForecastHour{ Time: v.Time, Icon: v.Icon, Temp: v.Temperature, } hourly = append(hourly, *n) } output.DailyForecast = daily output.ForcastToday = hourly jsonStr, _ := json.Marshal(output) log.Println("Forecast output") // log.Println(string(jsonStr)) return string(jsonStr) } func saveWeatherRecord(app *pocketbase.PocketBase, newWeather structs.FormattedWeather) error { log.Println("$$:saveWeatherRecord", newWeather.Lat, newWeather.Long) collection, err := app.Dao().FindCollectionByNameOrId("weather") if err != nil { log.Println(err) return err } latlow := newWeather.Lat - 0.005 lathigh := newWeather.Lat + 0.005 longlow := newWeather.Long - 0.005 longhigh := newWeather.Long + 0.005 record, recErr := app.Dao().FindRecordsByExpr("weather", dbx.NewExp("type = {:type} and lat >= {:latlow} and lat <= {:lathigh} and long >= {:longlow} and long <= {:longhigh}", dbx.Params{"latlow": latlow, "lathigh": lathigh, "longlow": longlow, "longhigh": longhigh, "type": newWeather.Type})) if recErr != nil { log.Println(recErr) return recErr } if len(record) == 0 { log.Println("-- Insert new weather record") record := models.NewRecord(collection) record.Set("lat", newWeather.Lat) record.Set("long", newWeather.Long) record.Set("data", newWeather.Data) record.Set("ts", newWeather.Ts) record.Set("type", newWeather.Type) if err := app.Dao().SaveRecord(record); err != nil { log.Println("ERROR!! saveWeatherRecord NewRecord") log.Println(err) return err } } else { log.Println("-- Update weather record") rec := record[0] rec.Set("lat", newWeather.Lat) rec.Set("long", newWeather.Long) rec.Set("data", newWeather.Data) rec.Set("ts", newWeather.Ts) if err := app.Dao().SaveRecord(rec); err != nil { log.Println("ERROR!! saveLocationRec SaveRecord") log.Println(err) return err } } return nil } /* type ForecastHour struct { Time int64 `json:"time"` Icon string `json:"icon"` Temp float64 `json:"temp"` } return &structs.FormattedLocation{ Lat: latlong.Lat, Long: latlong.Long, Country: address.Country, City: address.City, State: address.State, Zipcode: address.Postcode, StreetName: address.Street, CountryCode: address.CountryCode, County: address.County, Neighbourhood: address.Suburb, Village: "", Formatted: address.FormattedAddress, } type ForcastHour struct { Time int `json:"time"` Icon string `json:"icon"` Temp float64 `json:"temp"` } PirateWeather "alerts": [ { "title": "Wind Advisory issued January 24 at 9:25AM CST until January 24 at 6:00PM CST by NWS Corpus Christi TX", "regions": ["Live Oak", " Bee", " Goliad", " Victoria", " Jim Wells", " Inland Kleberg", " Inland Nueces", " Inland San Patricio", " Coastal Aransas", " Inland Refugio", " Inland Calhoun", " Coastal Kleberg", " Coastal Nueces", " Coastal San Patricio", " Aransas Islands", " Coastal Refugio", " Coastal Calhoun", " Kleberg Islands", " Nueces Islands", " Calhoun Islands"], "severity": "Moderate", "time": 1674573900, "expires": 1674604800, "description": "* WHAT...Southwest winds 25 to 30 mph with gusts up to 40 mph. * WHERE...Portions of South Texas. * WHEN...Until 6 PM CST this evening. * IMPACTS...Gusty winds could blow around unsecured objects. Tree limbs could be blown down and a few power outages may result.", "uri": "https://api.weather.gov/alerts/urn:oid:2.49.0.1.840.0.492c55233ef16d7a98a3337298c828b0f358ea34.001.1" }, ] */