Major restructuring in favour of current weather reading
- Renamed types from ObservationXXX to generic type names - Put generic types into their own files - Added CurrentWeatherByCoordinates - Added Source type and made it available for Temperature types
This commit is contained in:
parent
e44e6f6024
commit
80a4d74032
127
curweather.go
Normal file
127
curweather.go
Normal file
|
@ -0,0 +1,127 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// CurrentWeather represents the current weather API response
|
||||
type CurrentWeather struct {
|
||||
// Data holds the different APICurrentWeatherData points
|
||||
Data APICurrentWeatherData `json:"data"`
|
||||
// Latitude represents the GeoLocation latitude coordinates for the weather data
|
||||
Latitude float64 `json:"lat"`
|
||||
// Longitude represents the GeoLocation longitude coordinates for the weather data
|
||||
Longitude float64 `json:"lon"`
|
||||
// UnitSystem is the unit system that is used for the results (we default to metric)
|
||||
UnitSystem string `json:"systemOfUnits"`
|
||||
}
|
||||
|
||||
// APICurrentWeatherData holds the different data points of the CurrentWeather as
|
||||
// returned by the current weather API endpoints.
|
||||
//
|
||||
// Please keep in mind that different Station types return different values, therefore
|
||||
// all values are represented as pointer type returning nil if the data point in question
|
||||
// is not returned for the requested Station.
|
||||
type APICurrentWeatherData struct {
|
||||
/*
|
||||
// Dewpoint represents the dewpoint in °C
|
||||
Dewpoint *APIValue `json:"dewpoint,omitempty"`
|
||||
// DewPointMean represents the mean dewpoint in °C
|
||||
DewpointMean *APIValue `json:"dewpointMean,omitempty"`
|
||||
// GlobalRadiation10m represents the sum of global radiation over the last
|
||||
// 10 minutes in kJ/m²
|
||||
GlobalRadiation10m *APIValue `json:"globalRadiation10m,omitempty"`
|
||||
// GlobalRadiation1h represents the sum of global radiation over the last
|
||||
// 1 hour in kJ/m²
|
||||
GlobalRadiation1h *APIValue `json:"globalRadiation1h,omitempty"`
|
||||
// GlobalRadiation24h represents the sum of global radiation over the last
|
||||
// 24 hour in kJ/m²
|
||||
GlobalRadiation24h *APIValue `json:"globalRadiation24h,omitempty"`
|
||||
// HumidityRelative represents the relative humidity in percent
|
||||
HumidityRelative *APIValue `json:"humidityRelative,omitempty"`
|
||||
// Precipitation represents the current amount of precipitation
|
||||
Precipitation *APIValue `json:"prec"`
|
||||
// Precipitation10m represents the amount of precipitation over the last 10 minutes
|
||||
Precipitation10m *APIValue `json:"prec10m"`
|
||||
// Precipitation1h represents the amount of precipitation over the last hour
|
||||
Precipitation1h *APIValue `json:"prec1h"`
|
||||
// Precipitation24h represents the amount of precipitation over the last 24 hours
|
||||
Precipitation24h *APIValue `json:"prec24h"`
|
||||
// PressureMSL represents the pressure at mean sea level (MSL) in hPa
|
||||
PressureMSL *APIValue `json:"pressureMsl"`
|
||||
// PressureMSL represents the pressure at station level (QFE) in hPa
|
||||
PressureQFE *APIValue `json:"pressure"`
|
||||
|
||||
*/
|
||||
// Temperature represents the temperature in °C
|
||||
Temperature *APIValue `json:"temp,omitempty"`
|
||||
/*
|
||||
// TemperatureMax represents the maximum temperature in °C
|
||||
TemperatureMax *APIValue `json:"tempMax,omitempty"`
|
||||
// TemperatureMean represents the mean temperature in °C
|
||||
TemperatureMean *APIValue `json:"tempMean,omitempty"`
|
||||
// TemperatureMin represents the minimum temperature in °C
|
||||
TemperatureMin *APIValue `json:"tempMin,omitempty"`
|
||||
// Temperature5cm represents the temperature 5cm above ground in °C
|
||||
Temperature5cm *APIValue `json:"temp5cm,omitempty"`
|
||||
// Temperature5cm represents the minimum temperature 5cm above
|
||||
// ground in °C
|
||||
Temperature5cmMin *APIValue `json:"temp5cmMin,omitempty"`
|
||||
// Winddirection represents the direction from which the wind
|
||||
// originates in degree (0=N, 90=E, 180=S, 270=W)
|
||||
Winddirection *APIValue `json:"windDirection,omitempty"`
|
||||
// Windspeed represents the wind speed in knots
|
||||
Windspeed *APIValue `json:"windSpeed,omitempty"`
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
// CurrentWeatherByCoordinates returns the CurrentWeather values for the
|
||||
// given coordinates
|
||||
func (c *Client) CurrentWeatherByCoordinates(la, lo float64) (CurrentWeather, error) {
|
||||
var cw CurrentWeather
|
||||
u, err := url.Parse(fmt.Sprintf("%s/current/%f/%f", c.config.apiURL, la, lo))
|
||||
if err != nil {
|
||||
return cw, fmt.Errorf("failed to parse current weather URL: %w", err)
|
||||
}
|
||||
uq := u.Query()
|
||||
uq.Add("units", "metric")
|
||||
u.RawQuery = uq.Encode()
|
||||
|
||||
r, err := c.httpClient.Get(u.String())
|
||||
if err != nil {
|
||||
return cw, fmt.Errorf("API request failed: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(r, &cw); err != nil {
|
||||
return cw, fmt.Errorf("failed to unmarshal API response JSON: %w", err)
|
||||
}
|
||||
|
||||
return cw, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (cw CurrentWeather) Temperature() Temperature {
|
||||
if cw.Data.Temperature == nil {
|
||||
return Temperature{na: true}
|
||||
}
|
||||
v := Temperature{
|
||||
dt: cw.Data.Temperature.DateTime,
|
||||
n: FieldTemperature,
|
||||
s: SourceUnknown,
|
||||
v: cw.Data.Temperature.Value,
|
||||
}
|
||||
if cw.Data.Temperature.Source != nil {
|
||||
v.s = StringToSource(*cw.Data.Temperature.Source)
|
||||
}
|
||||
return v
|
||||
}
|
88
datatype.go
Normal file
88
datatype.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import "time"
|
||||
|
||||
// Enum for different Fieldname values
|
||||
const (
|
||||
// FieldDewpoint represents the Dewpoint data point
|
||||
FieldDewpoint Fieldname = iota
|
||||
// FieldDewpointMean represents the TemperatureMean data point
|
||||
FieldDewpointMean
|
||||
// FieldGlobalRadiation10m represents the GlobalRadiation10m data point
|
||||
FieldGlobalRadiation10m
|
||||
// FieldGlobalRadiation1h represents the GlobalRadiation1h data point
|
||||
FieldGlobalRadiation1h
|
||||
// FieldGlobalRadiation24h represents the GlobalRadiation24h data point
|
||||
FieldGlobalRadiation24h
|
||||
// FieldHumidityRelative represents the HumidityRelative data point
|
||||
FieldHumidityRelative
|
||||
// FieldPrecipitation represents the Precipitation data point
|
||||
FieldPrecipitation
|
||||
// FieldPrecipitation10m represents the Precipitation10m data point
|
||||
FieldPrecipitation10m
|
||||
// FieldPrecipitation1h represents the Precipitation1h data point
|
||||
FieldPrecipitation1h
|
||||
// FieldPrecipitation24h represents the Precipitation24h data point
|
||||
FieldPrecipitation24h
|
||||
// FieldPressureMSL represents the PressureMSL data point
|
||||
FieldPressureMSL
|
||||
// FieldPressureQFE represents the PressureQFE data point
|
||||
FieldPressureQFE
|
||||
// FieldTemperature represents the Temperature data point
|
||||
FieldTemperature
|
||||
// FieldTemperatureAtGround represents the TemperatureAtGround data point
|
||||
FieldTemperatureAtGround
|
||||
// FieldTemperatureAtGroundMin represents the TemperatureAtGroundMin data point
|
||||
FieldTemperatureAtGroundMin
|
||||
// FieldTemperatureMax represents the TemperatureMax data point
|
||||
FieldTemperatureMax
|
||||
// FieldTemperatureMean represents the TemperatureMean data point
|
||||
FieldTemperatureMean
|
||||
// FieldTemperatureMin represents the TemperatureMin data point
|
||||
FieldTemperatureMin
|
||||
// FieldWinddirection represents the Winddirection data point
|
||||
FieldWinddirection
|
||||
// FieldWindspeed represents the Windspeed data point
|
||||
FieldWindspeed
|
||||
)
|
||||
|
||||
// Enum for different Timespan values
|
||||
const (
|
||||
// TimespanCurrent represents the moment of the last observation
|
||||
TimespanCurrent Timespan = iota
|
||||
// Timespan10Min represents the last 10 minutes
|
||||
Timespan10Min
|
||||
// Timespan1Hour represents the last hour
|
||||
Timespan1Hour
|
||||
// Timespan24Hours represents the last 24 hours
|
||||
Timespan24Hours
|
||||
)
|
||||
|
||||
// APIValue is the JSON structure of the weather data that is returned by the
|
||||
// API endpoints
|
||||
type APIValue struct {
|
||||
DateTime time.Time `json:"dateTime"`
|
||||
Source *string `json:"source,omitempty"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
// Timespan is a type wrapper for an int type
|
||||
type Timespan int
|
||||
|
||||
// WeatherData is a type that holds weather (Observation, Current Weather) data and
|
||||
// can be wrapped into other types to provide type specific receiver methods
|
||||
type WeatherData struct {
|
||||
dt time.Time
|
||||
n Fieldname
|
||||
na bool
|
||||
s Source
|
||||
v float64
|
||||
}
|
||||
|
||||
// Fieldname is a type wrapper for an int for field names
|
||||
// of an Observation
|
||||
type Fieldname int
|
85
direction.go
Normal file
85
direction.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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 (t Direction) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Direction value was
|
||||
// available at time of query
|
||||
func (t Direction) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Direction in degrees
|
||||
// If the Direction is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Direction) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Direction type
|
||||
func (t Direction) String() string {
|
||||
return fmt.Sprintf("%.0f°", t.v)
|
||||
}
|
||||
|
||||
// Direction returns the abbreviation string for a given Direction type
|
||||
func (t Direction) Direction() string {
|
||||
if ds, ok := WindDirAbbrMap[t.v]; ok {
|
||||
return ds
|
||||
}
|
||||
return ErrUnsupportedDirection
|
||||
}
|
||||
|
||||
// DirectionFull returns the full string for a given Direction type
|
||||
func (t Direction) DirectionFull() string {
|
||||
if ds, ok := WindDirFullMap[t.v]; ok {
|
||||
return ds
|
||||
}
|
||||
return ErrUnsupportedDirection
|
||||
}
|
42
humidity.go
Normal file
42
humidity.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Humidity is a type wrapper of an WeatherData for holding humidity
|
||||
// values in WeatherData
|
||||
type Humidity WeatherData
|
||||
|
||||
// IsAvailable returns true if an Humidity value was
|
||||
// available at time of query
|
||||
func (t Humidity) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Humidity value was
|
||||
// available at time of query
|
||||
func (t Humidity) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Humidity type
|
||||
func (t Humidity) String() string {
|
||||
return fmt.Sprintf("%.1f%%", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Humidity
|
||||
// If the Humidity is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Humidity) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
607
observation.go
607
observation.go
|
@ -7,91 +7,8 @@ package meteologix
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// FieldDewpoint represents the Dewpoint data point
|
||||
FieldDewpoint ObservationFieldName = iota
|
||||
// FieldDewpointMean represents the TemperatureMean data point
|
||||
FieldDewpointMean
|
||||
// FieldGlobalRadiation10m represents the GlobalRadiation10m data point
|
||||
FieldGlobalRadiation10m
|
||||
// FieldGlobalRadiation1h represents the GlobalRadiation1h data point
|
||||
FieldGlobalRadiation1h
|
||||
// FieldGlobalRadiation24h represents the GlobalRadiation24h data point
|
||||
FieldGlobalRadiation24h
|
||||
// FieldHumidityRelative represents the HumidityRelative data point
|
||||
FieldHumidityRelative
|
||||
// FieldPrecipitation represents the Precipitation data point
|
||||
FieldPrecipitation
|
||||
// FieldPrecipitation10m represents the Precipitation10m data point
|
||||
FieldPrecipitation10m
|
||||
// FieldPrecipitation1h represents the Precipitation1h data point
|
||||
FieldPrecipitation1h
|
||||
// FieldPrecipitation24h represents the Precipitation24h data point
|
||||
FieldPrecipitation24h
|
||||
// FieldPressureMSL represents the PressureMSL data point
|
||||
FieldPressureMSL
|
||||
// FieldPressureQFE represents the PressureQFE data point
|
||||
FieldPressureQFE
|
||||
// FieldTemperature represents the Temperature data point
|
||||
FieldTemperature
|
||||
// FieldTemperatureAtGround represents the TemperatureAtGround data point
|
||||
FieldTemperatureAtGround
|
||||
// FieldTemperatureAtGroundMin represents the TemperatureAtGroundMin data point
|
||||
FieldTemperatureAtGroundMin
|
||||
// FieldTemperatureMax represents the TemperatureMax data point
|
||||
FieldTemperatureMax
|
||||
// FieldTemperatureMean represents the TemperatureMean data point
|
||||
FieldTemperatureMean
|
||||
// FieldTemperatureMin represents the TemperatureMin data point
|
||||
FieldTemperatureMin
|
||||
// FieldWinddirection represents the Winddirection data point
|
||||
FieldWinddirection
|
||||
// FieldWindspeed represents the Windspeed data point
|
||||
FieldWindspeed
|
||||
)
|
||||
|
||||
const (
|
||||
// TimespanCurrent represents the moment of the last observation
|
||||
TimespanCurrent Timespan = iota
|
||||
// Timespan10Min represents the last 10 minutes
|
||||
Timespan10Min
|
||||
// Timespan1Hour represents the last hour
|
||||
Timespan1Hour
|
||||
// Timespan24Hours represents the last 24 hours
|
||||
Timespan24Hours
|
||||
)
|
||||
|
||||
// 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",
|
||||
}
|
||||
|
||||
// ErrUnsupportedDirection is returned when a direction degree is given,
|
||||
// that is not resolvable
|
||||
var ErrUnsupportedDirection = "Unsupported direction"
|
||||
|
@ -100,8 +17,8 @@ var ErrUnsupportedDirection = "Unsupported direction"
|
|||
type Observation struct {
|
||||
// Altitude is the altitude of the station providing the Observation
|
||||
Altitude *int `json:"ele,omitempty"`
|
||||
// Data holds the different ObservationData points
|
||||
Data ObservationData `json:"data"`
|
||||
// Data holds the different APIObservationData points
|
||||
Data APIObservationData `json:"data"`
|
||||
// Name is the name of the Station providing the Observation
|
||||
Name string `json:"name"`
|
||||
// Latitude represents the GeoLocation latitude coordinates for the Station
|
||||
|
@ -112,110 +29,60 @@ type Observation struct {
|
|||
StationID string `json:"stationId"`
|
||||
}
|
||||
|
||||
// ObservationData holds the different data points of the Observation.
|
||||
// APIObservationData holds the different data points of the Observation as
|
||||
// returned by the station observation API endpoints.
|
||||
//
|
||||
// Please keep in mind that different Station types return different values, therefore
|
||||
// all values are represented as pointer type returning nil if the data point in question
|
||||
// is not returned for the requested Station.
|
||||
type ObservationData struct {
|
||||
type APIObservationData struct {
|
||||
// Dewpoint represents the dewpoint in °C
|
||||
Dewpoint *ObservationValue `json:"dewpoint,omitempty"`
|
||||
Dewpoint *APIValue `json:"dewpoint,omitempty"`
|
||||
// DewPointMean represents the mean dewpoint in °C
|
||||
DewpointMean *ObservationValue `json:"dewpointMean,omitempty"`
|
||||
DewpointMean *APIValue `json:"dewpointMean,omitempty"`
|
||||
// GlobalRadiation10m represents the sum of global radiation over the last
|
||||
// 10 minutes in kJ/m²
|
||||
GlobalRadiation10m *ObservationValue `json:"globalRadiation10m,omitempty"`
|
||||
GlobalRadiation10m *APIValue `json:"globalRadiation10m,omitempty"`
|
||||
// GlobalRadiation1h represents the sum of global radiation over the last
|
||||
// 1 hour in kJ/m²
|
||||
GlobalRadiation1h *ObservationValue `json:"globalRadiation1h,omitempty"`
|
||||
GlobalRadiation1h *APIValue `json:"globalRadiation1h,omitempty"`
|
||||
// GlobalRadiation24h represents the sum of global radiation over the last
|
||||
// 24 hour in kJ/m²
|
||||
GlobalRadiation24h *ObservationValue `json:"globalRadiation24h,omitempty"`
|
||||
GlobalRadiation24h *APIValue `json:"globalRadiation24h,omitempty"`
|
||||
// HumidityRelative represents the relative humidity in percent
|
||||
HumidityRelative *ObservationValue `json:"humidityRelative,omitempty"`
|
||||
HumidityRelative *APIValue `json:"humidityRelative,omitempty"`
|
||||
// Precipitation represents the current amount of precipitation
|
||||
Precipitation *ObservationValue `json:"prec"`
|
||||
Precipitation *APIValue `json:"prec"`
|
||||
// Precipitation10m represents the amount of precipitation over the last 10 minutes
|
||||
Precipitation10m *ObservationValue `json:"prec10m"`
|
||||
Precipitation10m *APIValue `json:"prec10m"`
|
||||
// Precipitation1h represents the amount of precipitation over the last hour
|
||||
Precipitation1h *ObservationValue `json:"prec1h"`
|
||||
Precipitation1h *APIValue `json:"prec1h"`
|
||||
// Precipitation24h represents the amount of precipitation over the last 24 hours
|
||||
Precipitation24h *ObservationValue `json:"prec24h"`
|
||||
Precipitation24h *APIValue `json:"prec24h"`
|
||||
// PressureMSL represents the pressure at mean sea level (MSL) in hPa
|
||||
PressureMSL *ObservationValue `json:"pressureMsl"`
|
||||
PressureMSL *APIValue `json:"pressureMsl"`
|
||||
// PressureMSL represents the pressure at station level (QFE) in hPa
|
||||
PressureQFE *ObservationValue `json:"pressure"`
|
||||
PressureQFE *APIValue `json:"pressure"`
|
||||
// Temperature represents the temperature in °C
|
||||
Temperature *ObservationValue `json:"temp,omitempty"`
|
||||
Temperature *APIValue `json:"temp,omitempty"`
|
||||
// TemperatureMax represents the maximum temperature in °C
|
||||
TemperatureMax *ObservationValue `json:"tempMax,omitempty"`
|
||||
TemperatureMax *APIValue `json:"tempMax,omitempty"`
|
||||
// TemperatureMean represents the mean temperature in °C
|
||||
TemperatureMean *ObservationValue `json:"tempMean,omitempty"`
|
||||
TemperatureMean *APIValue `json:"tempMean,omitempty"`
|
||||
// TemperatureMin represents the minimum temperature in °C
|
||||
TemperatureMin *ObservationValue `json:"tempMin,omitempty"`
|
||||
TemperatureMin *APIValue `json:"tempMin,omitempty"`
|
||||
// Temperature5cm represents the temperature 5cm above ground in °C
|
||||
Temperature5cm *ObservationValue `json:"temp5cm,omitempty"`
|
||||
Temperature5cm *APIValue `json:"temp5cm,omitempty"`
|
||||
// Temperature5cm represents the minimum temperature 5cm above
|
||||
// ground in °C
|
||||
Temperature5cmMin *ObservationValue `json:"temp5cmMin,omitempty"`
|
||||
Temperature5cmMin *APIValue `json:"temp5cmMin,omitempty"`
|
||||
// Winddirection represents the direction from which the wind
|
||||
// originates in degree (0=N, 90=E, 180=S, 270=W)
|
||||
Winddirection *ObservationValue `json:"windDirection,omitempty"`
|
||||
Winddirection *APIValue `json:"windDirection,omitempty"`
|
||||
// Windspeed represents the wind speed in knots
|
||||
Windspeed *ObservationValue `json:"windSpeed,omitempty"`
|
||||
Windspeed *APIValue `json:"windSpeed,omitempty"`
|
||||
}
|
||||
|
||||
// ObservationValue is the JSON structure of the Observation data that is
|
||||
// returned by the API endpoints
|
||||
type ObservationValue struct {
|
||||
DateTime time.Time `json:"dateTime"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
// ObservationField is a type that holds Observation data and can be wrapped
|
||||
// into other types to provide type specific receiver methods
|
||||
type ObservationField struct {
|
||||
dt time.Time
|
||||
n ObservationFieldName
|
||||
na bool
|
||||
v float64
|
||||
}
|
||||
|
||||
// ObservationFieldName is a type wrapper for an int for field names
|
||||
// of an Observation
|
||||
type ObservationFieldName int
|
||||
|
||||
// ObservationDirection is a type wrapper of an ObservationField for
|
||||
// holding directional values
|
||||
type ObservationDirection ObservationField
|
||||
|
||||
// ObservationHumidity is a type wrapper of an ObservationField for
|
||||
// holding humidity values
|
||||
type ObservationHumidity ObservationField
|
||||
|
||||
// ObservationPrecipitation is a type wrapper for a precipitation value
|
||||
// in an Observation
|
||||
type ObservationPrecipitation ObservationField
|
||||
|
||||
// ObservationPressure is a type wrapper for a pressure value
|
||||
// in an Observation
|
||||
type ObservationPressure ObservationField
|
||||
|
||||
// ObservationRadiation is a type wrapper of an ObservationField for
|
||||
// holding radiation values
|
||||
type ObservationRadiation ObservationField
|
||||
|
||||
// ObservationSpeed is a type wrapper of an ObservationField for
|
||||
// holding speed values
|
||||
type ObservationSpeed ObservationField
|
||||
|
||||
// ObservationTemperature is a type wrapper of an ObservationField for
|
||||
// holding temperature values
|
||||
type ObservationTemperature ObservationField
|
||||
|
||||
// Timespan is a type wrapper for an int type
|
||||
type Timespan int
|
||||
|
||||
// ObservationLatestByStationID returns the latest Observation values from the
|
||||
// given Station
|
||||
func (c *Client) ObservationLatestByStationID(si string) (Observation, error) {
|
||||
|
@ -247,185 +114,196 @@ func (c *Client) ObservationLatestByLocation(l string) (Observation, Station, er
|
|||
return o, s, err
|
||||
}
|
||||
|
||||
// Dewpoint returns the dewpoint data point as ObservationTemperature
|
||||
// Dewpoint returns the dewpoint data point as Temperature
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) Dewpoint() ObservationTemperature {
|
||||
func (o Observation) Dewpoint() Temperature {
|
||||
if o.Data.Dewpoint == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.Dewpoint.DateTime,
|
||||
n: FieldDewpoint,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Dewpoint.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// DewpointMean returns the mean dewpoint data point as ObservationTemperature.
|
||||
// DewpointMean returns the mean dewpoint data point as Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) DewpointMean() ObservationTemperature {
|
||||
func (o Observation) DewpointMean() Temperature {
|
||||
if o.Data.DewpointMean == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.DewpointMean.DateTime,
|
||||
n: FieldDewpointMean,
|
||||
s: SourceObservation,
|
||||
v: o.Data.DewpointMean.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// Temperature returns the temperature data point as ObservationTemperature.
|
||||
// Temperature returns the temperature data point as Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) Temperature() ObservationTemperature {
|
||||
func (o Observation) Temperature() Temperature {
|
||||
if o.Data.Temperature == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.Temperature.DateTime,
|
||||
n: FieldTemperature,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Temperature.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// TemperatureAtGround returns the temperature at ground level (5cm)
|
||||
// data point as ObservationTemperature.
|
||||
// data point as Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) TemperatureAtGround() ObservationTemperature {
|
||||
func (o Observation) TemperatureAtGround() Temperature {
|
||||
if o.Data.Temperature5cm == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.Temperature5cm.DateTime,
|
||||
n: FieldTemperatureAtGround,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Temperature5cm.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// TemperatureMax returns the maximum temperature so far data point as
|
||||
// ObservationTemperature.
|
||||
// Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) TemperatureMax() ObservationTemperature {
|
||||
func (o Observation) TemperatureMax() Temperature {
|
||||
if o.Data.TemperatureMax == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.TemperatureMax.DateTime,
|
||||
n: FieldTemperatureMax,
|
||||
s: SourceObservation,
|
||||
v: o.Data.TemperatureMax.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// TemperatureMin returns the minimum temperature so far data point as
|
||||
// ObservationTemperature.
|
||||
// Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) TemperatureMin() ObservationTemperature {
|
||||
func (o Observation) TemperatureMin() Temperature {
|
||||
if o.Data.TemperatureMin == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.TemperatureMin.DateTime,
|
||||
n: FieldTemperatureMin,
|
||||
s: SourceObservation,
|
||||
v: o.Data.TemperatureMin.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// TemperatureAtGroundMin returns the minimum temperature so far
|
||||
// at ground level (5cm) data point as ObservationTemperature
|
||||
// at ground level (5cm) data point as Temperature
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) TemperatureAtGroundMin() ObservationTemperature {
|
||||
func (o Observation) TemperatureAtGroundMin() Temperature {
|
||||
if o.Data.Temperature5cmMin == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.Temperature5cmMin.DateTime,
|
||||
n: FieldTemperatureAtGroundMin,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Temperature5cmMin.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// TemperatureMean returns the mean temperature data point as ObservationTemperature.
|
||||
// TemperatureMean returns the mean temperature data point as Temperature.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationTemperature in which the "not available" field will be
|
||||
// Temperature in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) TemperatureMean() ObservationTemperature {
|
||||
func (o Observation) TemperatureMean() Temperature {
|
||||
if o.Data.TemperatureMean == nil {
|
||||
return ObservationTemperature{na: true}
|
||||
return Temperature{na: true}
|
||||
}
|
||||
return ObservationTemperature{
|
||||
return Temperature{
|
||||
dt: o.Data.TemperatureMean.DateTime,
|
||||
n: FieldTemperatureMean,
|
||||
s: SourceObservation,
|
||||
v: o.Data.TemperatureMean.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// HumidityRelative returns the relative humidity data point as float64.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationHumidity in which the "not available" field will be
|
||||
// Humidity in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) HumidityRelative() ObservationHumidity {
|
||||
func (o Observation) HumidityRelative() Humidity {
|
||||
if o.Data.HumidityRelative == nil {
|
||||
return ObservationHumidity{na: true}
|
||||
return Humidity{na: true}
|
||||
}
|
||||
return ObservationHumidity{
|
||||
return Humidity{
|
||||
dt: o.Data.HumidityRelative.DateTime,
|
||||
n: FieldHumidityRelative,
|
||||
s: SourceObservation,
|
||||
v: o.Data.HumidityRelative.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// PressureMSL returns the relative pressure at mean seal level data point
|
||||
// as ObservationPressure.
|
||||
// as Pressure.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationPressure in which the "not available" field will be
|
||||
// Pressure in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) PressureMSL() ObservationPressure {
|
||||
func (o Observation) PressureMSL() Pressure {
|
||||
if o.Data.PressureMSL == nil {
|
||||
return ObservationPressure{na: true}
|
||||
return Pressure{na: true}
|
||||
}
|
||||
return ObservationPressure{
|
||||
return Pressure{
|
||||
dt: o.Data.PressureMSL.DateTime,
|
||||
n: FieldPressureMSL,
|
||||
s: SourceObservation,
|
||||
v: o.Data.PressureMSL.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// PressureQFE returns the relative pressure at mean seal level data point
|
||||
// as ObservationPressure.
|
||||
// as Pressure.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationPressure in which the "not available" field will be
|
||||
// Pressure in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) PressureQFE() ObservationPressure {
|
||||
func (o Observation) PressureQFE() Pressure {
|
||||
if o.Data.PressureQFE == nil {
|
||||
return ObservationPressure{na: true}
|
||||
return Pressure{na: true}
|
||||
}
|
||||
return ObservationPressure{
|
||||
return Pressure{
|
||||
dt: o.Data.PressureQFE.DateTime,
|
||||
n: FieldPressureQFE,
|
||||
s: SourceObservation,
|
||||
v: o.Data.PressureQFE.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// Precipitation returns the current amount of precipitation (mm) as
|
||||
// ObservationPrecipitation
|
||||
// Precipitation
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationPrecipitation in which the "not available" field will be
|
||||
// Precipitation in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) Precipitation(ts Timespan) ObservationPrecipitation {
|
||||
var df *ObservationValue
|
||||
var fn ObservationFieldName
|
||||
func (o Observation) Precipitation(ts Timespan) Precipitation {
|
||||
var df *APIValue
|
||||
var fn Fieldname
|
||||
switch ts {
|
||||
case TimespanCurrent:
|
||||
df = o.Data.Precipitation
|
||||
|
@ -440,27 +318,28 @@ func (o Observation) Precipitation(ts Timespan) ObservationPrecipitation {
|
|||
df = o.Data.Precipitation24h
|
||||
fn = FieldPrecipitation24h
|
||||
default:
|
||||
return ObservationPrecipitation{na: true}
|
||||
return Precipitation{na: true}
|
||||
}
|
||||
|
||||
if df == nil {
|
||||
return ObservationPrecipitation{na: true}
|
||||
return Precipitation{na: true}
|
||||
}
|
||||
return ObservationPrecipitation{
|
||||
return Precipitation{
|
||||
dt: df.DateTime,
|
||||
n: fn,
|
||||
s: SourceObservation,
|
||||
v: df.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalRadiation returns the current amount of global radiation as
|
||||
// ObservationRadiation
|
||||
// Radiation
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationRadiation in which the "not available" field will be
|
||||
// Radiation in which the "not available" field will be
|
||||
// true.
|
||||
func (o Observation) GlobalRadiation(ts Timespan) ObservationRadiation {
|
||||
var df *ObservationValue
|
||||
var fn ObservationFieldName
|
||||
func (o Observation) GlobalRadiation(ts Timespan) Radiation {
|
||||
var df *APIValue
|
||||
var fn Fieldname
|
||||
switch ts {
|
||||
case Timespan10Min:
|
||||
df = o.Data.GlobalRadiation10m
|
||||
|
@ -472,293 +351,47 @@ func (o Observation) GlobalRadiation(ts Timespan) ObservationRadiation {
|
|||
df = o.Data.GlobalRadiation24h
|
||||
fn = FieldGlobalRadiation24h
|
||||
default:
|
||||
return ObservationRadiation{na: true}
|
||||
return Radiation{na: true}
|
||||
}
|
||||
|
||||
if df == nil {
|
||||
return ObservationRadiation{na: true}
|
||||
return Radiation{na: true}
|
||||
}
|
||||
return ObservationRadiation{
|
||||
return Radiation{
|
||||
dt: df.DateTime,
|
||||
n: fn,
|
||||
s: SourceObservation,
|
||||
v: df.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// Winddirection returns the current direction from which the wind
|
||||
// originates in degree (0=N, 90=E, 180=S, 270=W) as ObservationDirection.
|
||||
// originates in degree (0=N, 90=E, 180=S, 270=W) as Direction.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationDirection in which the "not available" field will be true.
|
||||
func (o Observation) Winddirection() ObservationDirection {
|
||||
// Direction in which the "not available" field will be true.
|
||||
func (o Observation) Winddirection() Direction {
|
||||
if o.Data.Winddirection == nil {
|
||||
return ObservationDirection{na: true}
|
||||
return Direction{na: true}
|
||||
}
|
||||
return ObservationDirection{
|
||||
return Direction{
|
||||
dt: o.Data.Winddirection.DateTime,
|
||||
n: FieldWinddirection,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Winddirection.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// Windspeed returns the current windspeed data point as ObservationSpeed.
|
||||
// Windspeed returns the current windspeed data point as Speed.
|
||||
// If the data point is not available in the Observation it will return
|
||||
// ObservationSpeed in which the "not available" field will be true.
|
||||
func (o Observation) Windspeed() ObservationSpeed {
|
||||
// Speed in which the "not available" field will be true.
|
||||
func (o Observation) Windspeed() Speed {
|
||||
if o.Data.Windspeed == nil {
|
||||
return ObservationSpeed{na: true}
|
||||
return Speed{na: true}
|
||||
}
|
||||
return ObservationSpeed{
|
||||
return Speed{
|
||||
dt: o.Data.Windspeed.DateTime,
|
||||
n: FieldWindspeed,
|
||||
s: SourceObservation,
|
||||
v: o.Data.Windspeed.Value,
|
||||
}
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationTemperature value was
|
||||
// available at time of query
|
||||
func (t ObservationTemperature) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationTemperature value was
|
||||
// available at time of query
|
||||
func (t ObservationTemperature) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationTemperature
|
||||
// If the ObservationTemperature is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationTemperature) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationTemperature type
|
||||
func (t ObservationTemperature) String() string {
|
||||
return fmt.Sprintf("%.1f°C", t.v)
|
||||
}
|
||||
|
||||
// Celsius returns the ObservationTemperature value in Celsius
|
||||
func (t ObservationTemperature) Celsius() float64 {
|
||||
return t.v
|
||||
}
|
||||
|
||||
// CelsiusString returns the ObservationTemperature value as Celsius
|
||||
// formated string.
|
||||
//
|
||||
// This is an alias for the fmt.Stringer interface
|
||||
func (t ObservationTemperature) CelsiusString() string {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Fahrenheit returns the ObservationTemperature value in Fahrenheit
|
||||
func (t ObservationTemperature) Fahrenheit() float64 {
|
||||
return t.v*9/5 + 32
|
||||
}
|
||||
|
||||
// FahrenheitString returns the ObservationTemperature value as Fahrenheit
|
||||
// formated string.
|
||||
func (t ObservationTemperature) FahrenheitString() string {
|
||||
return fmt.Sprintf("%.1f°F", t.Fahrenheit())
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationHumidity value was
|
||||
// available at time of query
|
||||
func (t ObservationHumidity) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationHumidity value was
|
||||
// available at time of query
|
||||
func (t ObservationHumidity) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationHumidity type
|
||||
func (t ObservationHumidity) String() string {
|
||||
return fmt.Sprintf("%.1f%%", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationHumidity
|
||||
// If the ObservationHumidity is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationHumidity) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationPrecipitation value was
|
||||
// available at time of query
|
||||
func (t ObservationPrecipitation) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationPrecipitation value was
|
||||
// available at time of query
|
||||
func (t ObservationPrecipitation) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationPrecipitation type
|
||||
func (t ObservationPrecipitation) String() string {
|
||||
return fmt.Sprintf("%.1fmm", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationPrecipitation
|
||||
// If the ObservationPrecipitation is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationPrecipitation) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationPressure value was
|
||||
// available at time of query
|
||||
func (t ObservationPressure) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationPressure value was
|
||||
// available at time of query
|
||||
func (t ObservationPressure) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationPressure type
|
||||
func (t ObservationPressure) String() string {
|
||||
return fmt.Sprintf("%.1fhPa", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationPressure
|
||||
// If the ObservationPressure is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationPressure) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationRadiation value was
|
||||
// available at time of query
|
||||
func (t ObservationRadiation) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationRadiation value was
|
||||
// available at time of query
|
||||
func (t ObservationRadiation) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationRadiation
|
||||
// If the ObservationRadiation is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationRadiation) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationRadiation type
|
||||
func (t ObservationRadiation) String() string {
|
||||
return fmt.Sprintf("%.0fkJ/m²", t.v)
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationSpeed value was
|
||||
// available at time of query
|
||||
func (t ObservationSpeed) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationSpeed value was
|
||||
// available at time of query
|
||||
func (t ObservationSpeed) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationSpeed in knots
|
||||
// If the ObservationSpeed is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationSpeed) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationSpeed type
|
||||
func (t ObservationSpeed) String() string {
|
||||
return fmt.Sprintf("%.0fkn", t.v)
|
||||
}
|
||||
|
||||
// KMH returns the ObservationSpeed value in km/h
|
||||
func (t ObservationSpeed) KMH() float64 {
|
||||
return t.v * 1.852
|
||||
}
|
||||
|
||||
// KMHString returns the ObservationSpeed value as formatted string in km/h
|
||||
func (t ObservationSpeed) KMHString() string {
|
||||
return fmt.Sprintf("%.1fkm/h", t.KMH())
|
||||
}
|
||||
|
||||
// MPH returns the ObservationSpeed value in mi/h
|
||||
func (t ObservationSpeed) MPH() float64 {
|
||||
return t.v * 1.151
|
||||
}
|
||||
|
||||
// MPHString returns the ObservationSpeed value as formatted string in mi/h
|
||||
func (t ObservationSpeed) MPHString() string {
|
||||
return fmt.Sprintf("%.1fmi/h", t.MPH())
|
||||
}
|
||||
|
||||
// IsAvailable returns true if an ObservationDirection value was
|
||||
// available at time of query
|
||||
func (t ObservationDirection) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an ObservationDirection value was
|
||||
// available at time of query
|
||||
func (t ObservationDirection) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an ObservationDirection in degrees
|
||||
// If the ObservationDirection is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t ObservationDirection) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ObservationDirection type
|
||||
func (t ObservationDirection) String() string {
|
||||
return fmt.Sprintf("%.0f°", t.v)
|
||||
}
|
||||
|
||||
// Direction returns the abbreviation string for a given ObservationDirection type
|
||||
func (t ObservationDirection) Direction() string {
|
||||
if ds, ok := WindDirAbbrMap[t.v]; ok {
|
||||
return ds
|
||||
}
|
||||
return ErrUnsupportedDirection
|
||||
}
|
||||
|
||||
// DirectionFull returns the full string for a given ObservationDirection type
|
||||
func (t ObservationDirection) DirectionFull() string {
|
||||
if ds, ok := WindDirFullMap[t.v]; ok {
|
||||
return ds
|
||||
}
|
||||
return ErrUnsupportedDirection
|
||||
}
|
||||
|
|
|
@ -135,17 +135,17 @@ func TestClient_ObservationLatestByStationID_Dewpoint(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
dp *ObservationTemperature
|
||||
dp *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationTemperature{
|
||||
{"K-Botanischer Garten", "199942", &Temperature{
|
||||
dt: time.Date(2023, 0o5, 15, 20, 10, 0, 0, time.UTC),
|
||||
v: 10.1,
|
||||
}},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{
|
||||
{"K-Stammheim", "H744", &Temperature{
|
||||
dt: time.Date(2023, 0o5, 15, 19, 30, 0, 0, time.UTC),
|
||||
v: 9.7,
|
||||
}},
|
||||
{"All data fields", "all", &ObservationTemperature{
|
||||
{"All data fields", "all", &Temperature{
|
||||
dt: time.Date(2023, 0o5, 17, 7, 40, 0, 0, time.UTC),
|
||||
v: 6.5,
|
||||
}},
|
||||
|
@ -197,11 +197,11 @@ func TestClient_ObservationLatestByStationID_DewpointMean(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 8.3}},
|
||||
{"All data fields", "all", &Temperature{v: 8.3}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -245,17 +245,17 @@ func TestClient_ObservationLatestByStationID_HumidityRealtive(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
h *ObservationHumidity
|
||||
h *Humidity
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationHumidity{
|
||||
{"K-Botanischer Garten", "199942", &Humidity{
|
||||
dt: time.Date(2023, 0o5, 15, 20, 10, 0, 0, time.UTC),
|
||||
v: 80,
|
||||
}},
|
||||
{"K-Stammheim", "H744", &ObservationHumidity{
|
||||
{"K-Stammheim", "H744", &Humidity{
|
||||
dt: time.Date(2023, 0o5, 15, 19, 30, 0, 0, time.UTC),
|
||||
v: 73,
|
||||
}},
|
||||
{"All data fields", "all", &ObservationHumidity{
|
||||
{"All data fields", "all", &Humidity{
|
||||
dt: time.Date(2023, 0o5, 17, 7, 40, 0, 0, time.UTC),
|
||||
v: 72,
|
||||
}},
|
||||
|
@ -306,17 +306,17 @@ func TestClient_ObservationLatestByStationID_PrecipitationCurrent(t *testing.T)
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation precipitation
|
||||
p *ObservationPrecipitation
|
||||
p *Precipitation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPrecipitation{
|
||||
{"K-Botanischer Garten", "199942", &Precipitation{
|
||||
dt: time.Date(2023, 0o5, 15, 18, 0, 0, 0, time.UTC),
|
||||
v: 0,
|
||||
}},
|
||||
{"K-Stammheim", "H744", &ObservationPrecipitation{
|
||||
{"K-Stammheim", "H744", &Precipitation{
|
||||
dt: time.Date(2023, 0o5, 15, 19, 30, 0, 0, time.UTC),
|
||||
v: 0,
|
||||
}},
|
||||
{"All data fields", "all", &ObservationPrecipitation{
|
||||
{"All data fields", "all", &Precipitation{
|
||||
dt: time.Date(2023, 0o5, 17, 7, 30, 0, 0, time.UTC),
|
||||
v: 0.1,
|
||||
}},
|
||||
|
@ -369,11 +369,11 @@ func TestClient_ObservationLatestByStationID_Precipitation10m(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation precipitation
|
||||
p *ObservationPrecipitation
|
||||
p *Precipitation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPrecipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &ObservationPrecipitation{v: 0}},
|
||||
{"All data fields", "all", &ObservationPrecipitation{v: 0.5}},
|
||||
{"K-Botanischer Garten", "199942", &Precipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &Precipitation{v: 0}},
|
||||
{"All data fields", "all", &Precipitation{v: 0.5}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -418,11 +418,11 @@ func TestClient_ObservationLatestByStationID_Precipitation1h(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation precipitation
|
||||
p *ObservationPrecipitation
|
||||
p *Precipitation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPrecipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &ObservationPrecipitation{v: 0}},
|
||||
{"All data fields", "all", &ObservationPrecipitation{v: 10.3}},
|
||||
{"K-Botanischer Garten", "199942", &Precipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &Precipitation{v: 0}},
|
||||
{"All data fields", "all", &Precipitation{v: 10.3}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -467,11 +467,11 @@ func TestClient_ObservationLatestByStationID_Precipitation24h(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation precipitation
|
||||
p *ObservationPrecipitation
|
||||
p *Precipitation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPrecipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &ObservationPrecipitation{v: 0}},
|
||||
{"All data fields", "all", &ObservationPrecipitation{v: 32.12}},
|
||||
{"K-Botanischer Garten", "199942", &Precipitation{v: 0}},
|
||||
{"K-Stammheim", "H744", &Precipitation{v: 0}},
|
||||
{"All data fields", "all", &Precipitation{v: 32.12}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -538,11 +538,11 @@ func TestClient_ObservationLatestByStationID_Temperature(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationTemperature{v: 13.4}},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{v: 14.4}},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 10.8}},
|
||||
{"K-Botanischer Garten", "199942", &Temperature{v: 13.4}},
|
||||
{"K-Stammheim", "H744", &Temperature{v: 14.4}},
|
||||
{"All data fields", "all", &Temperature{v: 10.8}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -586,11 +586,11 @@ func TestClient_ObservationLatestByStationID_TemperatureAtGround(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{v: 14.3}},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 15.4}},
|
||||
{"K-Stammheim", "H744", &Temperature{v: 14.3}},
|
||||
{"All data fields", "all", &Temperature{v: 15.4}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -634,11 +634,11 @@ func TestClient_ObservationLatestByStationID_TemperatureMin(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationTemperature{v: 12.3}},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{v: 11.9}},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 6.2}},
|
||||
{"K-Botanischer Garten", "199942", &Temperature{v: 12.3}},
|
||||
{"K-Stammheim", "H744", &Temperature{v: 11.9}},
|
||||
{"All data fields", "all", &Temperature{v: 6.2}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -682,11 +682,11 @@ func TestClient_ObservationLatestByStationID_TemperatureMax(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationTemperature{v: 20.5}},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{v: 20.7}},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 12.4}},
|
||||
{"K-Botanischer Garten", "199942", &Temperature{v: 20.5}},
|
||||
{"K-Stammheim", "H744", &Temperature{v: 20.7}},
|
||||
{"All data fields", "all", &Temperature{v: 12.4}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -730,11 +730,11 @@ func TestClient_ObservationLatestByStationID_TemperatureAtGroundMin(t *testing.T
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", &ObservationTemperature{v: 12.8}},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 3.7}},
|
||||
{"K-Stammheim", "H744", &Temperature{v: 12.8}},
|
||||
{"All data fields", "all", &Temperature{v: 3.7}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -778,11 +778,11 @@ func TestClient_ObservationLatestByStationID_TemperatureMean(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
t *ObservationTemperature
|
||||
t *Temperature
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationTemperature{v: 16.3}},
|
||||
{"All data fields", "all", &Temperature{v: 16.3}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -826,14 +826,14 @@ func TestClient_ObservationLatestByStationID_PressureMSL(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
p *ObservationPressure
|
||||
p *Pressure
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPressure{
|
||||
{"K-Botanischer Garten", "199942", &Pressure{
|
||||
dt: time.Date(2023, 0o5, 15, 20, 10, 0, 0, time.UTC),
|
||||
v: 1015.5,
|
||||
}},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationPressure{
|
||||
{"All data fields", "all", &Pressure{
|
||||
dt: time.Date(2023, 0o5, 17, 7, 40, 0, 0, time.UTC),
|
||||
v: 1026.3,
|
||||
}},
|
||||
|
@ -884,11 +884,11 @@ func TestClient_ObservationLatestByStationID_PressureQFE(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
p *ObservationPressure
|
||||
p *Pressure
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationPressure{v: 1010.2}},
|
||||
{"K-Botanischer Garten", "199942", &Pressure{v: 1010.2}},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationPressure{v: 1020.9}},
|
||||
{"All data fields", "all", &Pressure{v: 1020.9}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -954,14 +954,14 @@ func TestClient_ObservationLatestByStationID_GlobalRadiation10m(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation radiation
|
||||
p *ObservationRadiation
|
||||
p *Radiation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationRadiation{
|
||||
{"K-Botanischer Garten", "199942", &Radiation{
|
||||
dt: time.Date(2023, 0o5, 15, 20, 10, 0, 0, time.UTC),
|
||||
v: 0,
|
||||
}},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationRadiation{
|
||||
{"All data fields", "all", &Radiation{
|
||||
dt: time.Date(2023, 0o5, 17, 7, 40, 0, 0, time.UTC),
|
||||
v: 62,
|
||||
}},
|
||||
|
@ -1013,11 +1013,11 @@ func TestClient_ObservationLatestByStationID_GlobalRadiation1h(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation radiation
|
||||
p *ObservationRadiation
|
||||
p *Radiation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationRadiation{v: 0}},
|
||||
{"K-Botanischer Garten", "199942", &Radiation{v: 0}},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationRadiation{v: 200}},
|
||||
{"All data fields", "all", &Radiation{v: 200}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -1062,11 +1062,11 @@ func TestClient_ObservationLatestByStationID_GlobalRadiation24h(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation radiation
|
||||
p *ObservationRadiation
|
||||
p *Radiation
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", &ObservationRadiation{v: 774}},
|
||||
{"K-Botanischer Garten", "199942", &Radiation{v: 774}},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationRadiation{v: 756}},
|
||||
{"All data fields", "all", &Radiation{v: 756}},
|
||||
{"No data fields", "none", nil},
|
||||
}
|
||||
c := New(withMockAPI())
|
||||
|
@ -1111,11 +1111,11 @@ func TestClient_ObservationLatestByStationID_Winddirection(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
p *ObservationDirection
|
||||
p *Direction
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationDirection{
|
||||
{"All data fields", "all", &Direction{
|
||||
dt: time.Date(2023, 0o5, 21, 11, 30, 0, 0, time.UTC),
|
||||
v: 90,
|
||||
}},
|
||||
|
@ -1166,11 +1166,11 @@ func TestClient_ObservationLatestByStationID_Windspeed(t *testing.T) {
|
|||
// Station ID
|
||||
sid string
|
||||
// Observation dewpoint
|
||||
p *ObservationSpeed
|
||||
p *Speed
|
||||
}{
|
||||
{"K-Botanischer Garten", "199942", nil},
|
||||
{"K-Stammheim", "H744", nil},
|
||||
{"All data fields", "all", &ObservationSpeed{
|
||||
{"All data fields", "all", &Speed{
|
||||
dt: time.Date(2023, 0o5, 21, 11, 30, 0, 0, time.UTC),
|
||||
v: 15,
|
||||
}},
|
||||
|
@ -1257,21 +1257,21 @@ func TestObservationTemperature_String(t *testing.T) {
|
|||
ff := "%.1f°F"
|
||||
for _, tc := range tt {
|
||||
t.Run(fmt.Sprintf("%.2f°C", tc.c), func(t *testing.T) {
|
||||
ot := ObservationTemperature{v: tc.c}
|
||||
ot := Temperature{v: tc.c}
|
||||
if ot.Celsius() != tc.c {
|
||||
t.Errorf("ObservationTemperature.Celsius failed, expected: %f, got: %f", tc.c,
|
||||
t.Errorf("Temperature.Celsius failed, expected: %f, got: %f", tc.c,
|
||||
ot.Celsius())
|
||||
}
|
||||
if ot.CelsiusString() != fmt.Sprintf(cf, tc.c) {
|
||||
t.Errorf("ObservationTemperature.CelsiusString failed, expected: %s, got: %s",
|
||||
t.Errorf("Temperature.CelsiusString failed, expected: %s, got: %s",
|
||||
fmt.Sprintf(cf, tc.c), ot.CelsiusString())
|
||||
}
|
||||
if ot.Fahrenheit() != tc.f {
|
||||
t.Errorf("ObservationTemperature.Fahrenheit failed, expected: %f, got: %f", tc.f,
|
||||
t.Errorf("Temperature.Fahrenheit failed, expected: %f, got: %f", tc.f,
|
||||
ot.Fahrenheit())
|
||||
}
|
||||
if ot.FahrenheitString() != fmt.Sprintf(ff, tc.f) {
|
||||
t.Errorf("ObservationTemperature.FahrenheitString failed, expected: %s, got: %s",
|
||||
t.Errorf("Temperature.FahrenheitString failed, expected: %s, got: %s",
|
||||
fmt.Sprintf(ff, tc.f), ot.FahrenheitString())
|
||||
}
|
||||
})
|
||||
|
@ -1298,29 +1298,29 @@ func TestObservationSpeed_Conversion(t *testing.T) {
|
|||
mphf := "%.1fmi/h"
|
||||
for _, tc := range tt {
|
||||
t.Run(fmt.Sprintf("%.0fkn", tc.kn), func(t *testing.T) {
|
||||
os := ObservationSpeed{v: tc.kn}
|
||||
os := Speed{v: tc.kn}
|
||||
if os.Value() != tc.kn {
|
||||
t.Errorf("ObservationSpeed.Value failed, expected: %f, got: %f", tc.kn,
|
||||
t.Errorf("Speed.Value failed, expected: %f, got: %f", tc.kn,
|
||||
os.Value())
|
||||
}
|
||||
if os.String() != fmt.Sprintf(knf, tc.kn) {
|
||||
t.Errorf("ObservationSpeed.String failed, expected: %s, got: %s",
|
||||
t.Errorf("Speed.String failed, expected: %s, got: %s",
|
||||
fmt.Sprintf(knf, tc.kn), os.String())
|
||||
}
|
||||
if os.KMH() != tc.kmh {
|
||||
t.Errorf("ObservationSpeed.KMH failed, expected: %f, got: %f", tc.kmh,
|
||||
t.Errorf("Speed.KMH failed, expected: %f, got: %f", tc.kmh,
|
||||
os.KMH())
|
||||
}
|
||||
if os.KMHString() != fmt.Sprintf(kmhf, tc.kmh) {
|
||||
t.Errorf("ObservationSpeed.KMHString failed, expected: %s, got: %s",
|
||||
t.Errorf("Speed.KMHString failed, expected: %s, got: %s",
|
||||
fmt.Sprintf(kmhf, tc.kmh), os.KMHString())
|
||||
}
|
||||
if os.MPH() != tc.mph {
|
||||
t.Errorf("ObservationSpeed.MPH failed, expected: %f, got: %f", tc.mph,
|
||||
t.Errorf("Speed.MPH failed, expected: %f, got: %f", tc.mph,
|
||||
os.MPH())
|
||||
}
|
||||
if os.MPHString() != fmt.Sprintf(mphf, tc.mph) {
|
||||
t.Errorf("ObservationSpeed.MPHString failed, expected: %s, got: %s",
|
||||
t.Errorf("Speed.MPHString failed, expected: %s, got: %s",
|
||||
fmt.Sprintf(mphf, tc.mph), os.MPHString())
|
||||
}
|
||||
})
|
||||
|
@ -1370,9 +1370,9 @@ func TestObservationDirection_Direction(t *testing.T) {
|
|||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(fmt.Sprintf("%.2f° => %s", tc.d, tc.ds), func(t *testing.T) {
|
||||
d := ObservationDirection{v: tc.d}
|
||||
d := Direction{v: tc.d}
|
||||
if d.Direction() != tc.ds {
|
||||
t.Errorf("ObservationDirection.Direction failed, expected: %s, got: %s",
|
||||
t.Errorf("Direction.Direction failed, expected: %s, got: %s",
|
||||
tc.ds, d.Direction())
|
||||
}
|
||||
})
|
||||
|
@ -1422,9 +1422,9 @@ func TestObservationDirection_DirectionFull(t *testing.T) {
|
|||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(fmt.Sprintf("%.2f° => %s", tc.d, tc.ds), func(t *testing.T) {
|
||||
d := ObservationDirection{v: tc.d}
|
||||
d := Direction{v: tc.d}
|
||||
if d.DirectionFull() != tc.ds {
|
||||
t.Errorf("ObservationDirection.Direction failed, expected: %s, got: %s",
|
||||
t.Errorf("Direction.Direction failed, expected: %s, got: %s",
|
||||
tc.ds, d.DirectionFull())
|
||||
}
|
||||
})
|
||||
|
|
42
precipitation.go
Normal file
42
precipitation.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Precipitation is a type wrapper of an WeatherData for holding precipitation
|
||||
// values in WeatherData
|
||||
type Precipitation WeatherData
|
||||
|
||||
// IsAvailable returns true if an Precipitation value was
|
||||
// available at time of query
|
||||
func (t Precipitation) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Precipitation value was
|
||||
// available at time of query
|
||||
func (t Precipitation) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Precipitation type
|
||||
func (t Precipitation) String() string {
|
||||
return fmt.Sprintf("%.1fmm", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Precipitation
|
||||
// If the Precipitation is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Precipitation) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
42
pressure.go
Normal file
42
pressure.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pressure is a type wrapper of an WeatherData for holding pressure
|
||||
// values in WeatherData
|
||||
type Pressure WeatherData
|
||||
|
||||
// IsAvailable returns true if an Pressure value was
|
||||
// available at time of query
|
||||
func (t Pressure) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Pressure value was
|
||||
// available at time of query
|
||||
func (t Pressure) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Pressure type
|
||||
func (t Pressure) String() string {
|
||||
return fmt.Sprintf("%.1fhPa", t.v)
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Pressure
|
||||
// If the Pressure is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Pressure) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
42
radiation.go
Normal file
42
radiation.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Radiation is a type wrapper of an WeatherData for holding radiation
|
||||
// values in WeatherData
|
||||
type Radiation WeatherData
|
||||
|
||||
// IsAvailable returns true if an Radiation value was
|
||||
// available at time of query
|
||||
func (t Radiation) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Radiation value was
|
||||
// available at time of query
|
||||
func (t Radiation) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Radiation
|
||||
// If the Radiation is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Radiation) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Radiation type
|
||||
func (t Radiation) String() string {
|
||||
return fmt.Sprintf("%.0fkJ/m²", t.v)
|
||||
}
|
54
source.go
Normal file
54
source.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package meteologix
|
||||
|
||||
import "strings"
|
||||
|
||||
// Enum of different weather data sources
|
||||
const (
|
||||
// SourceObservation represent observations from weather stations (high precision)
|
||||
SourceObservation = iota
|
||||
// SourceAnalysis represents weather data based on analysis (medium precision)
|
||||
SourceAnalysis
|
||||
// SourceForecast represents weather data based on weather forcecasts
|
||||
SourceForecast
|
||||
// SourceMixed represents weather data based on mixed sources
|
||||
SourceMixed
|
||||
// SourceUnknown represents weather data based on unknown sources
|
||||
SourceUnknown
|
||||
)
|
||||
|
||||
// Source is a type wrapper for an int type to enum different weather sources
|
||||
type Source int
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Source type
|
||||
func (s Source) String() string {
|
||||
switch s {
|
||||
case SourceObservation:
|
||||
return "Observation"
|
||||
case SourceAnalysis:
|
||||
return "Analysis"
|
||||
case SourceForecast:
|
||||
return "Forecast"
|
||||
case SourceMixed:
|
||||
return "Mixed"
|
||||
case SourceUnknown:
|
||||
return "Unknown"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// StringToSource converts a given source string to a Source type
|
||||
func StringToSource(s string) Source {
|
||||
switch strings.ToLower(s) {
|
||||
case "observation":
|
||||
return SourceObservation
|
||||
case "analysis":
|
||||
return SourceAnalysis
|
||||
case "forecast":
|
||||
return SourceForecast
|
||||
case "mixed":
|
||||
return SourceMixed
|
||||
default:
|
||||
return SourceUnknown
|
||||
}
|
||||
}
|
62
speed.go
Normal file
62
speed.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Speed is a type wrapper of an WeatherData for holding speed
|
||||
// values in WeatherData
|
||||
type Speed WeatherData
|
||||
|
||||
// IsAvailable returns true if an Speed value was
|
||||
// available at time of query
|
||||
func (t Speed) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Speed value was
|
||||
// available at time of query
|
||||
func (t Speed) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Speed in knots
|
||||
// If the Speed is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Speed) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Speed type
|
||||
func (t Speed) String() string {
|
||||
return fmt.Sprintf("%.0fkn", t.v)
|
||||
}
|
||||
|
||||
// KMH returns the Speed value in km/h
|
||||
func (t Speed) KMH() float64 {
|
||||
return t.v * 1.852
|
||||
}
|
||||
|
||||
// KMHString returns the Speed value as formatted string in km/h
|
||||
func (t Speed) KMHString() string {
|
||||
return fmt.Sprintf("%.1fkm/h", t.KMH())
|
||||
}
|
||||
|
||||
// MPH returns the Speed value in mi/h
|
||||
func (t Speed) MPH() float64 {
|
||||
return t.v * 1.151
|
||||
}
|
||||
|
||||
// MPHString returns the Speed value as formatted string in mi/h
|
||||
func (t Speed) MPHString() string {
|
||||
return fmt.Sprintf("%.1fmi/h", t.MPH())
|
||||
}
|
72
temperature.go
Normal file
72
temperature.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: 2023 Winni Neessen <wn@neessen.dev>
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package meteologix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Temperature is a type wrapper of an WeatherData for holding temperature
|
||||
// values in WeatherData
|
||||
type Temperature WeatherData
|
||||
|
||||
// IsAvailable returns true if an Temperature value was
|
||||
// available at time of query
|
||||
func (t Temperature) IsAvailable() bool {
|
||||
return !t.na
|
||||
}
|
||||
|
||||
// DateTime returns true if an Temperature value was
|
||||
// available at time of query
|
||||
func (t Temperature) DateTime() time.Time {
|
||||
return t.dt
|
||||
}
|
||||
|
||||
// Value returns the float64 value of an Temperature
|
||||
// If the Temperature is not available in the Observation
|
||||
// Vaule will return math.NaN instead.
|
||||
func (t Temperature) Value() float64 {
|
||||
if t.na {
|
||||
return math.NaN()
|
||||
}
|
||||
return t.v
|
||||
}
|
||||
|
||||
// Source returns the Source of an Temperature
|
||||
// If the Source is not available it will return SourceUnknown
|
||||
func (t Temperature) Source() Source {
|
||||
return t.s
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Temperature type
|
||||
func (t Temperature) String() string {
|
||||
return fmt.Sprintf("%.1f°C", t.v)
|
||||
}
|
||||
|
||||
// Celsius returns the Temperature value in Celsius
|
||||
func (t Temperature) Celsius() float64 {
|
||||
return t.v
|
||||
}
|
||||
|
||||
// CelsiusString returns the Temperature value as Celsius
|
||||
// formated string.
|
||||
//
|
||||
// This is an alias for the fmt.Stringer interface
|
||||
func (t Temperature) CelsiusString() string {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
// Fahrenheit returns the Temperature value in Fahrenheit
|
||||
func (t Temperature) Fahrenheit() float64 {
|
||||
return t.v*9/5 + 32
|
||||
}
|
||||
|
||||
// FahrenheitString returns the Temperature value as Fahrenheit
|
||||
// formated string.
|
||||
func (t Temperature) FahrenheitString() string {
|
||||
return fmt.Sprintf("%.1f°F", t.Fahrenheit())
|
||||
}
|
Loading…
Reference in a new issue