2023-05-22 17:13:36 +02:00
|
|
|
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package meteologix
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
2023-05-24 12:25:24 +02:00
|
|
|
"sort"
|
2023-05-22 17:13:36 +02:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2023-05-24 12:25:24 +02:00
|
|
|
const (
|
2023-06-27 16:49:22 +02:00
|
|
|
// DirectionMinAngle is the minimum angel for a direction
|
|
|
|
DirectionMinAngle = 0
|
|
|
|
// DirectionMaxAngle is the maximum angel for a direction
|
|
|
|
DirectionMaxAngle = 360
|
2023-05-24 12:25:24 +02:00
|
|
|
)
|
|
|
|
|
2023-05-22 17:13:36 +02:00
|
|
|
// WindDirAbbrMap is a map to associate a wind direction degree value with
|
|
|
|
// the abbreviated direction string
|
|
|
|
var WindDirAbbrMap = map[float64]string{
|
|
|
|
0: "N", 11.25: "NbE", 22.5: "NNE", 33.75: "NEbN", 45: "NE", 56.25: "NEbE",
|
|
|
|
67.5: "ENE", 78.75: "EbN", 90: "E", 101.25: "EbS", 112.5: "ESE", 123.75: "SEbE",
|
|
|
|
135: "SE", 146.25: "SEbS", 157.5: "SSE", 168.75: "SbE", 180: "S",
|
|
|
|
191.25: "SbW", 202.5: "SSW", 213.75: "SWbS", 225: "SW", 236.25: "SWbW",
|
|
|
|
247.5: "WSW", 258.75: "WbS", 270: "W", 281.25: "WbN", 292.5: "WNW",
|
|
|
|
303.75: "NWbW", 315: "NW", 326.25: "NWbN", 337.5: "NNW", 348.75: "NbW",
|
|
|
|
}
|
|
|
|
|
|
|
|
// WindDirFullMap is a map to associate a wind direction degree value with
|
|
|
|
// the full direction string
|
|
|
|
var WindDirFullMap = map[float64]string{
|
|
|
|
0: "North", 11.25: "North by East", 22.5: "North-Northeast",
|
|
|
|
33.75: "Northeast by North", 45: "Northeast", 56.25: "Northeast by East",
|
|
|
|
67.5: "East-Northeast", 78.75: "East by North", 90: "East",
|
|
|
|
101.25: "East by South", 112.5: "East-Southeast", 123.75: "Southeast by East",
|
|
|
|
135: "Southeast", 146.25: "Southeast by South", 157.5: "South-Southeast",
|
|
|
|
168.75: "South by East", 180: "South", 191.25: "South by West",
|
|
|
|
202.5: "South-Southwest", 213.75: "Southwest by South", 225: "Southwest",
|
|
|
|
236.25: "Southwest by West", 247.5: "West-Southwest", 258.75: "West by South",
|
|
|
|
270: "West", 281.25: "West by North", 292.5: "West-Northwest",
|
|
|
|
303.75: "Northwest by West", 315: "Northwest", 326.25: "Northwest by North",
|
|
|
|
337.5: "North-Northwest", 348.75: "North by West",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Direction is a type wrapper of an WeatherData for holding directional
|
|
|
|
// values in WeatherData
|
|
|
|
type Direction WeatherData
|
|
|
|
|
|
|
|
// IsAvailable returns true if an Direction value was
|
|
|
|
// available at time of query
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) IsAvailable() bool {
|
|
|
|
return !d.na
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// DateTime returns true if an Direction value was
|
|
|
|
// available at time of query
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) DateTime() time.Time {
|
|
|
|
return d.dt
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Value returns the float64 value of an Direction in degrees
|
2023-06-26 12:21:36 +02:00
|
|
|
// If the Direction is not available in the WeatherData
|
2023-05-22 17:13:36 +02:00
|
|
|
// Vaule will return math.NaN instead.
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) Value() float64 {
|
|
|
|
if d.na {
|
2023-05-22 17:13:36 +02:00
|
|
|
return math.NaN()
|
|
|
|
}
|
2023-05-24 22:00:47 +02:00
|
|
|
return d.fv
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// String satisfies the fmt.Stringer interface for the Direction type
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) String() string {
|
2023-05-24 22:00:47 +02:00
|
|
|
return fmt.Sprintf("%.0f°", d.fv)
|
2023-05-23 20:00:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Source returns the Source of a Direction
|
|
|
|
// If the Source is not available it will return SourceUnknown
|
|
|
|
func (d Direction) Source() Source {
|
|
|
|
return d.s
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Direction returns the abbreviation string for a given Direction type
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) Direction() string {
|
2023-06-27 16:49:22 +02:00
|
|
|
if d.fv < DirectionMinAngle || d.fv > DirectionMaxAngle {
|
2023-05-24 12:25:24 +02:00
|
|
|
return ErrUnsupportedDirection
|
|
|
|
}
|
2023-05-24 22:00:47 +02:00
|
|
|
if ds, ok := WindDirAbbrMap[d.fv]; ok {
|
2023-05-22 17:13:36 +02:00
|
|
|
return ds
|
|
|
|
}
|
2023-05-24 22:00:47 +02:00
|
|
|
return findDirection(d.fv, WindDirAbbrMap)
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// DirectionFull returns the full string for a given Direction type
|
2023-05-23 20:00:42 +02:00
|
|
|
func (d Direction) DirectionFull() string {
|
2023-06-27 16:49:22 +02:00
|
|
|
if d.fv < DirectionMinAngle || d.fv > DirectionMaxAngle {
|
2023-05-24 12:25:24 +02:00
|
|
|
return ErrUnsupportedDirection
|
|
|
|
}
|
2023-05-24 22:00:47 +02:00
|
|
|
if ds, ok := WindDirFullMap[d.fv]; ok {
|
2023-05-22 17:13:36 +02:00
|
|
|
return ds
|
|
|
|
}
|
2023-05-24 22:00:47 +02:00
|
|
|
return findDirection(d.fv, WindDirFullMap)
|
2023-05-24 12:25:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// findDirection takes a Direction and tries to estimate the nearest
|
|
|
|
// direction string from a map
|
|
|
|
func findDirection(v float64, m map[float64]string) string {
|
|
|
|
ks := make([]float64, 0, len(m))
|
|
|
|
for k := range m {
|
|
|
|
ks = append(ks, k)
|
|
|
|
}
|
|
|
|
sort.Float64s(ks)
|
|
|
|
|
|
|
|
sv := 0.0
|
|
|
|
ev := 0.0
|
|
|
|
for i := range ks {
|
|
|
|
if v > ks[i] {
|
|
|
|
sv = ks[i]
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if v < ks[i] {
|
|
|
|
ev = ks[i]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2023-06-27 16:49:22 +02:00
|
|
|
sr := v - sv
|
|
|
|
er := ev - v
|
2023-05-24 12:25:24 +02:00
|
|
|
if er > sr {
|
|
|
|
return m[sv]
|
|
|
|
}
|
|
|
|
return m[ev]
|
2023-05-22 17:13:36 +02:00
|
|
|
}
|