Moved the standards package out of internal/ so it can be imported by downstream consumers.
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
This commit is contained in:
@@ -11,8 +11,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AlertsNormalizer converts:
|
// AlertsNormalizer converts:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForecastNormalizer converts:
|
// ForecastNormalizer converts:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObservationNormalizer converts:
|
// ObservationNormalizer converts:
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
omcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
omcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForecastNormalizer converts:
|
// ForecastNormalizer converts:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
omcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
omcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObservationNormalizer converts:
|
// ObservationNormalizer converts:
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
normcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/normalizers/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
||||||
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObservationNormalizer converts:
|
// ObservationNormalizer converts:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AlertsSource polls an NWS alerts endpoint and emits a RAW alerts Event.
|
// AlertsSource polls an NWS alerts endpoint and emits a RAW alerts Event.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForecastSource polls an NWS forecast endpoint (narrative or hourly) and emits a RAW forecast Event.
|
// ForecastSource polls an NWS forecast endpoint (narrative or hourly) and emits a RAW forecast Event.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
nwscommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/nws"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObservationSource polls an NWS station observation endpoint and emits a RAW observation Event.
|
// ObservationSource polls an NWS station observation endpoint and emits a RAW observation Event.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ForecastSource polls an Open-Meteo hourly forecast endpoint and emits one RAW Forecast Event.
|
// ForecastSource polls an Open-Meteo hourly forecast endpoint and emits one RAW Forecast Event.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openmeteo"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObservationSource polls an Open-Meteo endpoint and emits one RAW Observation Event.
|
// ObservationSource polls an Open-Meteo endpoint and emits one RAW Observation Event.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"gitea.maximumdirect.net/ejr/feedkit/event"
|
"gitea.maximumdirect.net/ejr/feedkit/event"
|
||||||
owcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openweather"
|
owcommon "gitea.maximumdirect.net/ejr/weatherfeeder/internal/providers/openweather"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/sources/common"
|
||||||
"gitea.maximumdirect.net/ejr/weatherfeeder/internal/standards"
|
"gitea.maximumdirect.net/ejr/weatherfeeder/standards"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ObservationSource struct {
|
type ObservationSource struct {
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
package standards
|
|
||||||
|
|
||||||
import "gitea.maximumdirect.net/ejr/weatherfeeder/model"
|
|
||||||
|
|
||||||
// This file provides small, shared helper functions for reasoning about WMO codes.
|
|
||||||
// These are intentionally "coarse" categories that are useful for business logic,
|
|
||||||
// dashboards, and alerting decisions.
|
|
||||||
//
|
|
||||||
// Example uses:
|
|
||||||
// - jogging suitability: precipitation? thunderstorm? freezing precip?
|
|
||||||
// - quick glance: "is it cloudy?" "is there any precip?"
|
|
||||||
// - downstream normalizers / aggregators
|
|
||||||
|
|
||||||
func IsThunderstorm(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
case 95, 96, 99:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsHail(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
case 96, 99:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsFog(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
case 45, 48:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPrecipitation returns true if the code represents any precipitation
|
|
||||||
// (drizzle, rain, snow, showers, etc.).
|
|
||||||
func IsPrecipitation(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
// Drizzle
|
|
||||||
case 51, 53, 55, 56, 57:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Rain
|
|
||||||
case 61, 63, 65, 66, 67:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Snow
|
|
||||||
case 71, 73, 75, 77:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Showers
|
|
||||||
case 80, 81, 82, 85, 86:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Thunderstorm (often includes rain/hail)
|
|
||||||
case 95, 96, 99:
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsRainFamily(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
// Drizzle + freezing drizzle
|
|
||||||
case 51, 53, 55, 56, 57:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Rain + freezing rain
|
|
||||||
case 61, 63, 65, 66, 67:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Rain showers
|
|
||||||
case 80, 81, 82:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Thunderstorm often implies rain
|
|
||||||
case 95, 96, 99:
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSnowFamily(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
// Snow and related
|
|
||||||
case 71, 73, 75, 77:
|
|
||||||
return true
|
|
||||||
|
|
||||||
// Snow showers
|
|
||||||
case 85, 86:
|
|
||||||
return true
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFreezingPrecip returns true if the code represents freezing drizzle/rain.
|
|
||||||
func IsFreezingPrecip(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
case 56, 57, 66, 67:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSkyOnly returns true for codes that represent "sky condition only"
|
|
||||||
// (clear/mostly/partly/cloudy) rather than fog/precip/etc.
|
|
||||||
func IsSkyOnly(code model.WMOCode) bool {
|
|
||||||
switch code {
|
|
||||||
case 0, 1, 2, 3:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
// -----------------------
|
// -----------------------
|
||||||
// For readability and stability, canonical payloads (weather.* schemas) should not emit
|
// For readability and stability, canonical payloads (weather.* schemas) should not emit
|
||||||
// noisy floating-point representations. weatherfeeder enforces this by rounding float
|
// noisy floating-point representations. weatherfeeder enforces this by rounding float
|
||||||
// values in canonical payloads to 2 digits after the decimal point at normalization
|
// values in canonical payloads to 4 digits after the decimal point at normalization
|
||||||
// finalization time.
|
// finalization time.
|
||||||
//
|
//
|
||||||
// Provider-specific decoding helpers and quirks live in internal/providers/<provider>.
|
// Provider-specific decoding helpers and quirks live in internal/providers/<provider>.
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
package standards
|
package standards
|
||||||
|
|
||||||
|
// This file provides small, shared helper functions for reasoning about WMO codes.
|
||||||
|
// These are intentionally "coarse" categories that are useful for business logic,
|
||||||
|
// dashboards, and alerting decisions.
|
||||||
|
//
|
||||||
|
// Example uses:
|
||||||
|
// - jogging suitability: precipitation? thunderstorm? freezing precip?
|
||||||
|
// - quick glance: "is it cloudy?" "is there any precip?"
|
||||||
|
// - downstream normalizers / aggregators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -100,3 +109,118 @@ func IsKnownWMO(code model.WMOCode) bool {
|
|||||||
_, ok := WMODescriptions[code]
|
_, ok := WMODescriptions[code]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsThunderstorm(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
case 95, 96, 99:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsHail(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
case 96, 99:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFog(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
case 45, 48:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrecipitation returns true if the code represents any precipitation
|
||||||
|
// (drizzle, rain, snow, showers, etc.).
|
||||||
|
func IsPrecipitation(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
// Drizzle
|
||||||
|
case 51, 53, 55, 56, 57:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Rain
|
||||||
|
case 61, 63, 65, 66, 67:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Snow
|
||||||
|
case 71, 73, 75, 77:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Showers
|
||||||
|
case 80, 81, 82, 85, 86:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Thunderstorm (often includes rain/hail)
|
||||||
|
case 95, 96, 99:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRainFamily(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
// Drizzle + freezing drizzle
|
||||||
|
case 51, 53, 55, 56, 57:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Rain + freezing rain
|
||||||
|
case 61, 63, 65, 66, 67:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Rain showers
|
||||||
|
case 80, 81, 82:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Thunderstorm often implies rain
|
||||||
|
case 95, 96, 99:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSnowFamily(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
// Snow and related
|
||||||
|
case 71, 73, 75, 77:
|
||||||
|
return true
|
||||||
|
|
||||||
|
// Snow showers
|
||||||
|
case 85, 86:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFreezingPrecip returns true if the code represents freezing drizzle/rain.
|
||||||
|
func IsFreezingPrecip(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
case 56, 57, 66, 67:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSkyOnly returns true for codes that represent "sky condition only"
|
||||||
|
// (clear/mostly/partly/cloudy) rather than fog/precip/etc.
|
||||||
|
func IsSkyOnly(code model.WMOCode) bool {
|
||||||
|
switch code {
|
||||||
|
case 0, 1, 2, 3:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user