Added SnowHeight and corrected comments

This commit is contained in:
Winni Neessen 2023-06-26 12:21:36 +02:00
parent 7bdf2de388
commit 8cb9754f69
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
14 changed files with 236 additions and 30 deletions

View file

@ -50,6 +50,8 @@ type APICurrentWeatherData struct {
PressureQFE *APIFloat `json:"pressure,omitempty"`
// SnowAmount represents the the amount of snow in kg/m3
SnowAmount *APIFloat `json:"snowAmount,omitempty"`
// SnowHeight represents the the height of snow in m
SnowHeight *APIFloat `json:"snowHeight,omitempty"`
// Temperature represents the temperature in °C
Temperature *APIFloat `json:"temp,omitempty"`
// WindDirection represents the direction from which the wind
@ -249,7 +251,7 @@ func (cw CurrentWeather) PressureQFE() Pressure {
return v
}
// SnowAmount returns the temperature data point as Density.
// SnowAmount returns the amount of snow data point as Density.
// If the data point is not available in the CurrentWeather it will return
// Density in which the "not available" field will be true.
func (cw CurrentWeather) SnowAmount() Density {
@ -268,6 +270,25 @@ func (cw CurrentWeather) SnowAmount() Density {
return v
}
// SnowHeight returns the snow height data point as Height.
// If the data point is not available in the CurrentWeather it will return
// Height in which the "not available" field will be true.
func (cw CurrentWeather) SnowHeight() Height {
if cw.Data.SnowHeight == nil {
return Height{na: true}
}
v := Height{
dt: cw.Data.SnowHeight.DateTime,
n: FieldSnowHeight,
s: SourceUnknown,
fv: cw.Data.SnowHeight.Value,
}
if cw.Data.SnowHeight.Source != nil {
v.s = StringToSource(*cw.Data.SnowHeight.Source)
}
return v
}
// Temperature returns the temperature data point as Temperature.
// If the data point is not available in the CurrentWeather it will return
// Temperature in which the "not available" field will be true.

View file

@ -626,6 +626,103 @@ func TestClient_CurrentWeatherByLocation_SnowAmount(t *testing.T) {
}
}
func TestClient_CurrentWeatherByLocation_SnowHeight(t *testing.T) {
tt := []struct {
// Location name
loc string
// CurWeather height
h *Height
}{
{"Ehrenfeld, Germany", &Height{
dt: time.Date(2023, 5, 23, 6, 0, 0, 0, time.UTC),
s: SourceAnalysis,
fv: 1.23,
}},
{"Berlin, Germany", &Height{
dt: time.Date(2023, 5, 23, 6, 0, 0, 0, time.UTC),
s: SourceAnalysis,
fv: 0.003,
}},
{"Neermoor, Germany", nil},
}
c := New(withMockAPI())
if c == nil {
t.Errorf("failed to create new Client, got nil")
return
}
for _, tc := range tt {
t.Run(tc.loc, func(t *testing.T) {
cw, err := c.CurrentWeatherByLocation(tc.loc)
if err != nil {
t.Errorf("CurrentWeatherByLocation failed: %s", err)
return
}
if tc.h != nil && tc.h.String() != cw.SnowHeight().String() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"string: %s, got: %s", tc.h.String(), cw.SnowHeight())
}
if tc.h != nil && tc.h.Value() != cw.SnowHeight().Value() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"float: %f, got: %f", tc.h.Value(), cw.SnowHeight().Value())
}
if tc.h != nil && tc.h.MeterString() != cw.SnowHeight().MeterString() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"string: %s, got: %s", tc.h.MeterString(), cw.SnowHeight().MeterString())
}
if tc.h != nil && tc.h.Meter() != cw.SnowHeight().Meter() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"float: %f, got: %f", tc.h.Meter(), cw.SnowHeight().Meter())
}
if tc.h != nil && tc.h.CentiMeterString() != cw.SnowHeight().CentiMeterString() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"string: %s, got: %s", tc.h.CentiMeterString(), cw.SnowHeight().CentiMeterString())
}
if tc.h != nil && tc.h.CentiMeter() != cw.SnowHeight().CentiMeter() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"float: %f, got: %f", tc.h.CentiMeter(), cw.SnowHeight().CentiMeter())
}
if tc.h != nil && tc.h.MilliMeterString() != cw.SnowHeight().MilliMeterString() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"string: %s, got: %s", tc.h.MilliMeterString(), cw.SnowHeight().MilliMeterString())
}
if tc.h != nil && tc.h.MilliMeter() != cw.SnowHeight().MilliMeter() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"float: %f, got: %f", tc.h.MilliMeter(), cw.SnowHeight().MilliMeter())
}
if tc.h != nil && cw.SnowHeight().Source() != tc.h.s {
t.Errorf("CurrentWeatherByLocation failed, expected source: %s, but got: %s",
tc.h.s, cw.SnowHeight().Source())
}
if tc.h != nil && tc.h.dt.Unix() != cw.SnowHeight().DateTime().Unix() {
t.Errorf("CurrentWeatherByLocation failed, expected datetime: %s, got: %s",
tc.h.dt.Format(time.RFC3339), cw.SnowHeight().DateTime().Format(time.RFC3339))
}
if tc.h == nil {
if cw.SnowHeight().IsAvailable() {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"to have no data, but got: %s", cw.SnowHeight())
}
if !math.IsNaN(cw.SnowHeight().Value()) {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"to return NaN, but got: %s", cw.SnowHeight().String())
}
if !math.IsNaN(cw.SnowHeight().Meter()) {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"to return NaN, but got: %f", cw.SnowHeight().Meter())
}
if !math.IsNaN(cw.SnowHeight().CentiMeter()) {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"to return NaN, but got: %f", cw.SnowHeight().CentiMeter())
}
if !math.IsNaN(cw.SnowHeight().MilliMeter()) {
t.Errorf("CurrentWeatherByLocation failed, expected snow height "+
"to return NaN, but got: %f", cw.SnowHeight().MilliMeter())
}
}
})
}
}
func TestClient_CurrentWeatherByLocation_Temperature(t *testing.T) {
tt := []struct {
// Location name

View file

@ -46,6 +46,8 @@ const (
FieldPressureQFE
// FieldSnowAmount represents the SnowAmount data point
FieldSnowAmount
// FieldSnowHeight represents the SnowHeight data point
FieldSnowHeight
// FieldSunrise represents the Sunrise data point
FieldSunrise
// FieldSunset represents the Sunset data point

View file

@ -38,7 +38,7 @@ func (d Density) Source() Source {
}
// Value returns the float64 value of an Density
// If the Density is not available in the Observation
// If the Density is not available in the WeatherData
// Vaule will return math.NaN instead.
func (d Density) Value() float64 {
if d.na {

View file

@ -62,7 +62,7 @@ func (d Direction) DateTime() time.Time {
}
// Value returns the float64 value of an Direction in degrees
// If the Direction is not available in the Observation
// If the Direction is not available in the WeatherData
// Vaule will return math.NaN instead.
func (d Direction) Value() float64 {
if d.na {

86
height.go Normal file
View file

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
//
// SPDX-License-Identifier: MIT
package meteologix
import (
"fmt"
"math"
"time"
)
// Height is a type wrapper of an WeatherData for holding height
// values in WeatherData (based on meters a default unit)
type Height WeatherData
// IsAvailable returns true if an Height value was
// available at time of query
func (h Height) IsAvailable() bool {
return !h.na
}
// DateTime returns true if an Height value was
// available at time of query
func (h Height) DateTime() time.Time {
return h.dt
}
// String satisfies the fmt.Stringer interface for the Height type
func (h Height) String() string {
return fmt.Sprintf("%.3fm", h.fv)
}
// Source returns the Source of Height
// If the Source is not available it will return SourceUnknown
func (h Height) Source() Source {
return h.s
}
// Value returns the float64 value of an Height
// If the Height is not available in the WeatherData
// Vaule will return math.NaN instead.
func (h Height) Value() float64 {
if h.na {
return math.NaN()
}
return h.fv
}
// Meter returns the Height type value as float64 in meters.
// This is an alias for the Value() method
func (h Height) Meter() float64 {
return h.Value()
}
// MeterString returns the Height type as formatted string in meters
// This is an alias for the String() method
func (h Height) MeterString() string {
return h.String()
}
// CentiMeter returns the Height type value as float64 in centimeters.
func (h Height) CentiMeter() float64 {
if h.na {
return math.NaN()
}
return h.fv / 100
}
// CentiMeterString returns the Height type as formatted string in centimeters
func (h Height) CentiMeterString() string {
return fmt.Sprintf("%.3fcm", h.CentiMeter())
}
// MilliMeter returns the Height type value as float64 in milliimeters.
func (h Height) MilliMeter() float64 {
if h.na {
return math.NaN()
}
return h.fv / 1000
}
// MilliMeterString returns the Height type as formatted string in millimeters
func (h Height) MilliMeterString() string {
return fmt.Sprintf("%.3fmm", h.MilliMeter())
}

View file

@ -38,7 +38,7 @@ func (h Humidity) Source() Source {
}
// Value returns the float64 value of an Humidity
// If the Humidity is not available in the Observation
// If the Humidity is not available in the WeatherData
// Vaule will return math.NaN instead.
func (h Humidity) Value() float64 {
if h.na {

View file

@ -38,7 +38,7 @@ func (p Precipitation) Source() Source {
}
// Value returns the float64 value of an Precipitation
// If the Precipitation is not available in the Observation
// If the Precipitation is not available in the WeatherData
// Vaule will return math.NaN instead.
func (p Precipitation) Value() float64 {
if p.na {

View file

@ -38,7 +38,7 @@ func (p Pressure) Source() Source {
}
// Value returns the float64 value of an Pressure
// If the Pressure is not available in the Observation
// If the Pressure is not available in the WeatherData
// Vaule will return math.NaN instead.
func (p Pressure) Value() float64 {
if p.na {

View file

@ -27,7 +27,7 @@ func (r Radiation) DateTime() time.Time {
}
// Value returns the float64 value of an Radiation
// If the Radiation is not available in the Observation
// If the Radiation is not available in the WeatherData
// Vaule will return math.NaN instead.
func (r Radiation) Value() float64 {
if r.na {

View file

@ -37,7 +37,7 @@ func (s Speed) DateTime() time.Time {
// Value returns the float64 value of an Speed in meters
// per second.
// If the Speed is not available in the Observation
// If the Speed is not available in the WeatherData
// Vaule will return math.NaN instead.
func (s Speed) Value() float64 {
if s.na {

View file

@ -18,12 +18,12 @@ import (
const DefaultRadius int = 10
const (
// PrecisionHigh is a high precision weather station
PrecisionHigh Precision = iota
// PrecisionMedium is a medium precision weather station
PrecisionMedium
// PrecisionLow is a low precision weather station
PrecisionLow
// PrecisionSuperHigh represents data of < ~4km resolution
PrecisionSuperHigh Precision = iota
// PrecisionHigh represents data of >= ~4km but < ~10km resolution
PrecisionHigh
// PrecisionStandard represents data of >= ~10km resolution
PrecisionStandard
// PrecisionUnknown is weather station of unknown precision
PrecisionUnknown
)
@ -157,12 +157,12 @@ func (p *Precision) UnmarshalJSON(s []byte) error {
v := string(s)
v = strings.ReplaceAll(v, `"`, ``)
switch strings.ToLower(v) {
case "super_high":
*p = PrecisionSuperHigh
case "high":
*p = PrecisionHigh
case "medium":
*p = PrecisionMedium
case "low":
*p = PrecisionLow
case "standard":
*p = PrecisionStandard
default:
*p = PrecisionUnknown
}
@ -172,12 +172,12 @@ func (p *Precision) UnmarshalJSON(s []byte) error {
// String satisfies the fmt.Stringer interface for the Precision type
func (p *Precision) String() string {
switch *p {
case PrecisionSuperHigh:
return "SUPER_HIGH"
case PrecisionHigh:
return "HIGH"
case PrecisionMedium:
return "MEDIUM"
case PrecisionLow:
return "LOW"
case PrecisionStandard:
return "STANDARD"
case PrecisionUnknown:
return "UNKNOWN"
default:

View file

@ -164,16 +164,16 @@ func TestPrecision_UnmarshalJSON(t *testing.T) {
// Should fail
sf bool
}{
{
"Super high precision", []byte(`{"precision":"SUPER_HIGH"}`), PrecisionSuperHigh,
false,
},
{
"High precision", []byte(`{"precision":"HIGH"}`), PrecisionHigh,
false,
},
{
"Medium precision", []byte(`{"precision":"MEDIUM"}`), PrecisionMedium,
false,
},
{
"Low precision", []byte(`{"precision":"LOW"}`), PrecisionLow,
"Standard precision", []byte(`{"precision":"STANDARD"}`), PrecisionStandard,
false,
},
{
@ -209,9 +209,9 @@ func TestPrecision_String(t *testing.T) {
// Expected string
es string
}{
{"Super high precision", PrecisionSuperHigh, "SUPER_HIGH"},
{"High precision", PrecisionHigh, "HIGH"},
{"Medium precision", PrecisionMedium, "MEDIUM"},
{"Low precision", PrecisionLow, "LOW"},
{"Standard precision", PrecisionStandard, "STANDARD"},
{"Unknown precision", PrecisionUnknown, "UNKNOWN"},
{"Undefined precision", 999, "UNKNOWN"},
}

View file

@ -27,7 +27,7 @@ func (t Temperature) DateTime() time.Time {
}
// Value returns the float64 value of an Temperature
// If the Temperature is not available in the Observation
// If the Temperature is not available in the WeatherData
// Vaule will return math.NaN instead.
func (t Temperature) Value() float64 {
if t.na {