go-pb-jubilee/weather/weather.go
Martin Donnelly 9b11de7902 init
2024-08-28 00:24:33 +01:00

335 lines
9.0 KiB
Go

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"
},
]
*/