feat: polish & windviz & deploy
This commit is contained in:
parent
81b8e763bd
commit
465ad00f7b
78 changed files with 20622 additions and 2154 deletions
92
internal/api/wind.go
Normal file
92
internal/api/wind.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"predictor-refactored/internal/windviz"
|
||||
apirest "predictor-refactored/pkg/rest"
|
||||
)
|
||||
|
||||
// GetWindMeta implements GET /api/v1/wind/meta.
|
||||
func (h *Handler) GetWindMeta(_ context.Context) (*apirest.WindMeta, error) {
|
||||
field := h.mgr.Active()
|
||||
if field == nil {
|
||||
return nil, apiError(http.StatusServiceUnavailable, "no dataset loaded")
|
||||
}
|
||||
return &apirest.WindMeta{
|
||||
Source: field.Source(),
|
||||
Epoch: field.Epoch().UTC(),
|
||||
DefaultStep: 1.0,
|
||||
MinStep: 0.25,
|
||||
SuggestedAltitudes: []int{0, 1000, 5000, 10000, 15000, 20000, 30000},
|
||||
Bbox: apirest.Region{MinLat: -90, MaxLat: 90, MinLng: 0, MaxLng: 360},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWindField implements GET /api/v1/wind/field.
|
||||
func (h *Handler) GetWindField(_ context.Context, params apirest.GetWindFieldParams) ([]apirest.WindComponent, error) {
|
||||
field := h.mgr.Active()
|
||||
if field == nil {
|
||||
return nil, apiError(http.StatusServiceUnavailable, "no dataset loaded")
|
||||
}
|
||||
|
||||
when := field.Epoch()
|
||||
if t, ok := params.Time.Get(); ok {
|
||||
when = t
|
||||
}
|
||||
req := windviz.Request{
|
||||
Time: float64(when.Unix()),
|
||||
Altitude: params.Altitude.Or(0),
|
||||
MinLat: params.MinLat.Or(0),
|
||||
MaxLat: params.MaxLat.Or(0),
|
||||
MinLng: params.MinLng.Or(0),
|
||||
MaxLng: params.MaxLng.Or(0),
|
||||
Step: params.Step.Or(0),
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("%s|%v|%.3f|%.3f|%.3f|%.3f|%.3f|%.3f",
|
||||
field.Source(), req.Time, req.Altitude, req.MinLat, req.MaxLat, req.MinLng, req.MaxLng, req.Step)
|
||||
if h.cache != nil {
|
||||
if cached, ok := h.cache.Get(key); ok {
|
||||
return windFieldToAPI(cached), nil
|
||||
}
|
||||
}
|
||||
|
||||
out, err := windviz.Rasterize(field, req)
|
||||
if err != nil {
|
||||
return nil, apiError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
if h.cache != nil {
|
||||
h.cache.Put(key, out)
|
||||
}
|
||||
return windFieldToAPI(out), nil
|
||||
}
|
||||
|
||||
// windFieldToAPI maps a rasterized field to the generated component slice.
|
||||
func windFieldToAPI(f windviz.Field) []apirest.WindComponent {
|
||||
out := make([]apirest.WindComponent, 0, len(f))
|
||||
for _, c := range f {
|
||||
out = append(out, apirest.WindComponent{
|
||||
Header: apirest.WindHeader{
|
||||
ParameterCategory: c.Header.ParameterCategory,
|
||||
ParameterNumber: c.Header.ParameterNumber,
|
||||
ParameterNumberName: apirest.NewOptString(c.Header.ParameterNumberName),
|
||||
ParameterUnit: apirest.NewOptString(c.Header.ParameterUnit),
|
||||
Nx: c.Header.Nx,
|
||||
Ny: c.Header.Ny,
|
||||
Lo1: c.Header.Lo1,
|
||||
La1: c.Header.La1,
|
||||
Lo2: c.Header.Lo2,
|
||||
La2: c.Header.La2,
|
||||
Dx: c.Header.Dx,
|
||||
Dy: c.Header.Dy,
|
||||
RefTime: c.Header.RefTime,
|
||||
ForecastTime: c.Header.ForecastTime,
|
||||
},
|
||||
Data: c.Data,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue