fix(input): correct lon mapping

This commit is contained in:
gili8420 2026-08-04 13:40:50 +09:00
parent 19557f3a62
commit 84d1664b29
20 changed files with 1197 additions and 137 deletions

View file

@ -205,21 +205,32 @@ func buildPolygon(spec ConstraintSpec, _ BuildDeps) (Constraint, error) {
return NewPolygon(spec.Vertices, mode, act, spec.Label), nil
}
func buildConstantRate(spec ModelSpec, _ BuildDeps) (BuiltModel, error) {
return BuiltModel{Model: ConstantRate(spec.Rate)}, nil
func buildConstantRate(spec ModelSpec, deps BuildDeps) (BuiltModel, error) {
if err := checkWind(spec.IncludeWind, deps); err != nil {
return BuiltModel{}, err
}
return BuiltModel{Model: addWind(ConstantRate(spec.Rate), spec.IncludeWind, deps)}, nil
}
func buildParachuteDescent(spec ModelSpec, _ BuildDeps) (BuiltModel, error) {
func buildParachuteDescent(spec ModelSpec, deps BuildDeps) (BuiltModel, error) {
if spec.SeaLevelRate <= 0 {
return BuiltModel{}, fmt.Errorf("parachute_descent requires positive sea_level_rate")
}
return BuiltModel{Model: ParachuteDescent(spec.SeaLevelRate)}, nil
if err := checkWind(spec.IncludeWind, deps); err != nil {
return BuiltModel{}, err
}
return BuiltModel{Model: addWind(ParachuteDescent(spec.SeaLevelRate), spec.IncludeWind, deps)}, nil
}
func buildWind(_ ModelSpec, deps BuildDeps) (BuiltModel, error) {
func buildWind(spec ModelSpec, deps BuildDeps) (BuiltModel, error) {
if deps.Wind == nil {
return BuiltModel{}, fmt.Errorf("wind model requires a loaded wind field")
}
// Refused rather than ignored: summing the wind into itself would double the
// drift, and a doubled wind is a wrong forecast that looks like a right one.
if spec.IncludeWind {
return BuiltModel{}, fmt.Errorf("wind model does not take include_wind: it would sum the wind into itself")
}
return BuiltModel{Model: WindTransport(deps.Wind, deps.Events)}, nil
}
@ -231,12 +242,17 @@ func buildPiecewise(spec ModelSpec, deps BuildDeps) (BuiltModel, error) {
return BuiltModel{}, fmt.Errorf("piecewise: unknown segment reference %q", s.Reference)
}
}
// Checked here, not in the closure below: the profile runner calls Build once
// per stage and has nowhere to return an error to.
if err := checkWind(spec.IncludeWind, deps); err != nil {
return BuiltModel{}, err
}
// Always build lazily: the profile runner supplies a StageContext before
// each stage, which is what resolves absolute / profile-relative /
// propagator-relative segment times uniformly.
return BuiltModel{
Build: func(ctx StageContext) Model {
return maybeAddWind(Piecewise(resolveSegments(spec.Segments, ctx)), spec.IncludeWind, deps)
return addWind(Piecewise(resolveSegments(spec.Segments, ctx)), spec.IncludeWind, deps)
},
}, nil
}
@ -266,12 +282,26 @@ func segmentBase(reference string, ctx StageContext) float64 {
}
}
// maybeAddWind sums a WindTransport model into base when the spec asks for it.
func maybeAddWind(base Model, includeWind bool, deps BuildDeps) Model {
if !includeWind {
return base
// checkWind reports whether include_wind can be satisfied at all.
//
// A request that asks for wind and cannot have it is an error. This used to
// return the wind-free model instead, which turned a missing wind field into a
// balloon that rose and fell on the spot, reported as a successful forecast.
func checkWind(includeWind bool, deps BuildDeps) error {
if includeWind && deps.Wind == nil {
return fmt.Errorf("include_wind requires a loaded wind field")
}
if deps.Wind == nil {
return nil
}
// addWind sums a WindTransport model into base when the spec asks for it.
//
// Callers must have called checkWind first. The split exists because the
// piecewise builder combines inside a closure the profile runner invokes per
// stage, with no way to surface an error — so validation happens at build time
// and combination stays infallible, rather than an error being swallowed there.
func addWind(base Model, includeWind bool, deps BuildDeps) Model {
if !includeWind {
return base
}
return Sum(base, WindTransport(deps.Wind, deps.Events))