feat(nws, normalizers): add NWS hourly forecast normalization and enforce canonical float rounding
- Implement full NWS hourly forecast normalizer (raw.nws.hourly.forecast.v1 → weather.forecast.v1) - Add GeoJSON forecast types and helpers for NWS gridpoint hourly payloads - Normalize temperatures, winds, humidity, PoP, and infer WMO condition codes from forecast text/icons - Treat forecast IssuedAt as EffectiveAt for stable, dedupe-friendly event IDs - Introduce project-wide float rounding at normalization finalization - Round all float values in canonical payloads to 2 decimal places - Apply consistently across pointers, slices, maps, and nested structs - Preserve opaque structs (e.g., time.Time) unchanged - Add SchemaRawNWSHourlyForecastV1 and align schema matching/comments - Clean up NWS helper organization and comments - Update documentation to reflect numeric wire-format and normalization policies This establishes a complete, deterministic hourly forecast pipeline for NWS and improves JSON output stability across all canonical weather schemas.
This commit is contained in:
@@ -11,39 +11,9 @@
|
||||
// - minimal abstractions (prefer straightforward functions)
|
||||
// - easy to unit test
|
||||
//
|
||||
// What belongs here
|
||||
// -----------------
|
||||
// Put code in internal/normalizers/common when it is:
|
||||
//
|
||||
// - potentially reusable by more than one provider
|
||||
// - provider-agnostic (no NWS/OpenWeather/Open-Meteo specific assumptions)
|
||||
// - stable, small, and readable
|
||||
//
|
||||
// Typical examples:
|
||||
// - unit conversion helpers (°F <-> °C, m/s <-> km/h, hPa <-> Pa, etc.)
|
||||
// - json.RawMessage payload extraction helpers (with good error messages)
|
||||
// - shared parsing helpers (timestamps, simple numeric coercions)
|
||||
// - generic fallbacks (e.g., mapping a human text description into a coarse canonical code),
|
||||
// so long as the logic truly applies across providers
|
||||
//
|
||||
// What does NOT belong here
|
||||
// -------------------------
|
||||
// Do NOT put the following in this package:
|
||||
//
|
||||
// - Normalizer implementations (types that satisfy feedkit/normalize.Normalizer)
|
||||
// - provider-specific JSON structs or mapping logic (put those under
|
||||
// internal/normalizers/<provider>/)
|
||||
// - network or filesystem I/O (sources fetch; normalizers transform)
|
||||
// - code that depends on event.Source naming, config fields, or driver-specific params
|
||||
//
|
||||
// Style and API guidelines
|
||||
// ------------------------
|
||||
// - Prefer small, single-purpose functions.
|
||||
// - Keep function names explicit (avoid clever generic “DoThing” helpers).
|
||||
// - Return typed errors with context (include schema/field names where helpful).
|
||||
// - Keep dependencies minimal: standard library + weatherfeeder packages only.
|
||||
// - Add unit tests for any non-trivial logic (especially parsing and fallbacks).
|
||||
//
|
||||
// Keeping this clean matters: common is shared by all providers, so complexity here
|
||||
// multiplies across the project.
|
||||
// Numeric wire policy
|
||||
// -------------------
|
||||
// Canonical payloads are intended for sinks/serialization. To keep output stable and readable,
|
||||
// weatherfeeder rounds floating-point values in canonical payloads to a small, fixed precision
|
||||
// at finalization time (see round.go).
|
||||
package common
|
||||
|
||||
Reference in New Issue
Block a user