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:
2026-01-16 10:28:32 -06:00
parent 0fcc536885
commit 2eb2d4b90f
11 changed files with 831 additions and 92 deletions

View File

@@ -5,11 +5,19 @@
// - Schema identifiers and versioning conventions (see schema.go).
// - Canonical interpretations / cross-provider mappings that are not specific to a
// single upstream API (e.g., shared code tables, text heuristics, unit policy).
// - Wire-format conventions for canonical payloads.
//
// Standards are used by both sources and normalizers. Keep this package free of
// provider-specific logic and free of dependencies on internal/sources/* or
// internal/normalizers/* to avoid import cycles.
//
// Wire-format conventions
// -----------------------
// For readability and stability, canonical payloads (weather.* schemas) should not emit
// noisy floating-point representations. weatherfeeder enforces this by rounding float
// values in canonical payloads to 2 digits after the decimal point at normalization
// finalization time.
//
// Provider-specific decoding helpers and quirks live in internal/providers/<provider>.
// Normalizer implementations and canonical mapping logic live in internal/normalizers/<provider>.
package standards