go-meteologix/direction.go
Winni Neessen d0905266e1
Fix typo and improve direction calculations
Correct typos related to Angle naming and update calculations for start and end ranges in direction mapping. This ensures accurate and valid directional values are used in the application.
2023-06-27 16:49:22 +02:00

135 lines
3.8 KiB
Go

// 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
func (d Direction) IsAvailable() bool {
return !d.na
}
// DateTime returns true if an Direction value was
// available at time of query
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.
func (d Direction) Value() float64 {
if d.na {
return math.NaN()
}
return d.fv
}
// String satisfies the fmt.Stringer interface for the Direction type
func (d Direction) String() string {
return fmt.Sprintf("%.0f°", d.fv)
}
// 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
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
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]
}