feat: polish & windviz & deploy
This commit is contained in:
parent
81b8e763bd
commit
465ad00f7b
78 changed files with 20622 additions and 2154 deletions
69
internal/weather/gfs/wind_test.go
Normal file
69
internal/weather/gfs/wind_test.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package gfs
|
||||
|
||||
import (
|
||||
"math"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// testVariant is a tiny cube (2 hours × 3 levels × 3 lat × 4 lng) used to
|
||||
// exercise the sampler without allocating a multi-gigabyte real dataset.
|
||||
func testVariant() *Variant {
|
||||
return &Variant{
|
||||
ID: "gfs-test",
|
||||
ResToken: "test",
|
||||
Resolution: 90, // 180/90+1 = 3 lats, 360/90 = 4 lngs
|
||||
HourStep: 3,
|
||||
MaxHour: 3, // 2 hours
|
||||
Pressures: []int{1000, 500, 100},
|
||||
PressuresPgrb2: []int{1000, 500, 100},
|
||||
PressuresPgrb2b: []int{},
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindSampler(t *testing.T) {
|
||||
v := testVariant()
|
||||
path := filepath.Join(t.TempDir(), "cube.bin")
|
||||
f, err := Create(path, v)
|
||||
if err != nil {
|
||||
t.Fatalf("Create: %v", err)
|
||||
}
|
||||
|
||||
// HGT increases with level so the altitude bisection has a gradient;
|
||||
// U and V are constant so interpolation must return them exactly.
|
||||
for h := range v.NumHours() {
|
||||
for lvl := range v.NumLevels() {
|
||||
for la := range v.NumLatitudes() {
|
||||
for ln := range v.NumLongitudes() {
|
||||
f.SetVal(h, lvl, VarHeight, la, ln, float32(lvl*1000))
|
||||
f.SetVal(h, lvl, VarWindU, la, ln, 7)
|
||||
f.SetVal(h, lvl, VarWindV, la, ln, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
f.Flush()
|
||||
f.Close()
|
||||
|
||||
epoch := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
rf, err := Open(path, v, epoch)
|
||||
if err != nil {
|
||||
t.Fatalf("Open: %v", err)
|
||||
}
|
||||
defer rf.Close()
|
||||
w := NewWind(rf)
|
||||
|
||||
// Query at the dataset epoch, equator, lng 45, altitude 500m (between
|
||||
// level 0 @ 0m and level 1 @ 1000m).
|
||||
s, err := w.Wind(float64(epoch.Unix()), 0, 45, 500)
|
||||
if err != nil {
|
||||
t.Fatalf("Wind: %v", err)
|
||||
}
|
||||
if math.Abs(s.U-7) > 1e-5 || math.Abs(s.V-3) > 1e-5 {
|
||||
t.Errorf("constant wind not recovered: got U=%v V=%v, want 7,3", s.U, s.V)
|
||||
}
|
||||
if s.AboveModel {
|
||||
t.Errorf("AboveModel should be false at altitude within model range")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue