go-meteologix/direction.go

135 lines
3.8 KiB
Go
Raw Normal View History

// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
//
// SPDX-License-Identifier: MIT
package meteologix
import (
"fmt"
"math"
"sort"
"time"
)
const (
// DirectionMinAngle is the minimum angel for a direction
DirectionMinAngle = 0
// DirectionMaxAngle is the maximum angel for a direction
DirectionMaxAngle = 360
)
// 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
}
// 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
}
// Value returns the float64 value of an Direction in degrees
// If the Direction is not available in the WeatherData
// Vaule will return math.NaN instead.
2023-05-23 20:00:42 +02:00
func (d Direction) Value() float64 {
if d.na {
return math.NaN()
}
return d.fv
}
// String satisfies the fmt.Stringer interface for the Direction type
2023-05-23 20:00:42 +02:00
func (d Direction) String() string {
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
}
// Direction returns the abbreviation string for a given Direction type
2023-05-23 20:00:42 +02:00
func (d Direction) Direction() string {
if d.fv < DirectionMinAngle || d.fv > DirectionMaxAngle {
return ErrUnsupportedDirection
}
if ds, ok := WindDirAbbrMap[d.fv]; ok {
return ds
}
return findDirection(d.fv, WindDirAbbrMap)
}
// DirectionFull returns the full string for a given Direction type
2023-05-23 20:00:42 +02:00
func (d Direction) DirectionFull() string {
if d.fv < DirectionMinAngle || d.fv > DirectionMaxAngle {
return ErrUnsupportedDirection
}
if ds, ok := WindDirFullMap[d.fv]; ok {
return ds
}
return findDirection(d.fv, WindDirFullMap)
}
// 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
}
}
sr := v - sv
er := ev - v
if er > sr {
return m[sv]
}
return m[ev]
}