335 lines
9.0 KiB
Go
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"
|
||
|
},
|
||
|
]
|
||
|
|
||
|
|
||
|
|
||
|
*/
|