1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
Fredrik Steen 90d672b2f2 Add krisinformation module
A module to parse the feed from: https://www.krisinformation.se/en

 Krisinformation.se is a web site run by the Swedish Civil Contingencies Agency
that compiles and convey warnings, alerts, and emergency information from
Swedish authorities to the public.

Features:

 - Filter on country (all messages)
 - Filter on county (only show county specific messages)
 - Distance (only show message within a certain distance)
 - Set a max age for displaying messages
 - Set a maximum number of messages displayed
2020-11-30 16:26:51 +01:00

194 lines
4.9 KiB
Go

package krisinformation
import (
"fmt"
"math"
"net/http"
"strconv"
"strings"
"time"
"github.com/wtfutil/wtf/logger"
"github.com/wtfutil/wtf/utils"
)
const (
krisinformationAPI = "https://api.krisinformation.se/v2/feed?format=json"
)
type Krisinformation []struct {
Identifier string `json:"Identifier"`
PushMessage string `json:"PushMessage"`
Updated time.Time `json:"Updated"`
Published time.Time `json:"Published"`
Headline string `json:"Headline"`
Preamble string `json:"Preamble"`
BodyText string `json:"BodyText"`
Area []struct {
Type string `json:"Type"`
Description string `json:"Description"`
Coordinate string `json:"Coordinate"`
GeometryInformation interface{} `json:"GeometryInformation"`
} `json:"Area"`
Web string `json:"Web"`
Language string `json:"Language"`
Event string `json:"Event"`
SenderName string `json:"SenderName"`
Push bool `json:"Push"`
BodyLinks []interface{} `json:"BodyLinks"`
SourceID int `json:"SourceID"`
IsVma bool `json:"IsVma"`
IsTestVma bool `json:"IsTestVma"`
}
// Client holds or configuration
type Client struct {
latitude float64
longitude float64
radius int
county string
country bool
}
// Item holds the interesting parts
type Item struct {
PushMessage string
HeadLine string
SenderName string
Country bool
County bool
Distance float64
Updated time.Time
}
//NewClient returns a new Client
func NewClient(latitude, longitude float64, radius int, county string, country bool) *Client {
return &Client{
latitude: latitude,
longitude: longitude,
radius: radius,
county: county,
country: country,
}
}
// getKrisinformation - return items that match either country, county or a radius
// Priority:
// - Country
// - County
// - Region
func (c *Client) getKrisinformation() (items []Item, err error) {
resp, err := http.Get(krisinformationAPI)
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
var data Krisinformation
err = utils.ParseJSON(&data, resp.Body)
if err != nil {
return nil, err
}
for i := range data {
for a := range data[i].Area {
// Country wide events
if c.country && data[i].Area[a].Type == "Country" {
item := Item{
PushMessage: data[i].PushMessage,
HeadLine: data[i].Headline,
SenderName: data[i].SenderName,
Country: true,
Updated: data[i].Updated,
}
items = append(items, item)
continue
}
// County specific events
if c.county != "" && data[i].Area[a].Type == "County" {
// We look for county in description
if strings.Contains(
strings.ToLower(data[i].Area[a].Description),
strings.ToLower(c.county),
) {
item := Item{
PushMessage: data[i].PushMessage,
HeadLine: data[i].Headline,
SenderName: data[i].SenderName,
County: true,
Updated: data[i].Updated,
}
items = append(items, item)
continue
}
}
if c.radius != -1 {
coords := data[i].Area[a].Coordinate
if coords == "" {
continue
}
buf := strings.Split(coords, " ")
latlon := strings.Split(buf[0], ",")
kris_latitude, err := strconv.ParseFloat(latlon[0], 32)
if err != nil {
return nil, err
}
kris_longitude, err := strconv.ParseFloat(latlon[1], 32)
if err != nil {
return nil, err
}
distance := DistanceInMeters(kris_latitude, kris_longitude, c.latitude, c.longitude)
logger.Log(fmt.Sprintf("Distance: %f", distance/1000)) // KM
if distance < float64(c.radius) {
item := Item{
PushMessage: data[i].PushMessage,
HeadLine: data[i].Headline,
SenderName: data[i].SenderName,
Distance: distance,
Updated: data[i].Updated,
}
items = append(items, item)
}
}
}
}
return items, nil
}
// haversin(θ) function
func hsin(theta float64) float64 {
return math.Pow(math.Sin(theta/2), 2)
}
// Distance function returns the distance (in meters) between two points of
// a given longitude and latitude relatively accurately (using a spherical
// approximation of the Earth) through the Haversin Distance Formula for
// great arc distance on a sphere with accuracy for small distances
//
// point coordinates are supplied in degrees and converted into rad. in the func
//
// http://en.wikipedia.org/wiki/Haversine_formula
func DistanceInMeters(lat1, lon1, lat2, lon2 float64) float64 {
// convert to radians
// must cast radius as float to multiply later
var la1, lo1, la2, lo2, r float64
la1 = lat1 * math.Pi / 180
lo1 = lon1 * math.Pi / 180
la2 = lat2 * math.Pi / 180
lo2 = lon2 * math.Pi / 180
r = 6378100 // Earth radius in METERS
// calculate
h := hsin(la2-la1) + math.Cos(la1)*math.Cos(la2)*hsin(lo2-lo1)
return 2 * r * math.Asin(math.Sqrt(h))
}