feat: polish & windviz & deploy

This commit is contained in:
Anatoly Antonov 2026-05-30 06:29:39 +09:00
parent 81b8e763bd
commit 465ad00f7b
78 changed files with 20622 additions and 2154 deletions

63
internal/windviz/cache.go Normal file
View file

@ -0,0 +1,63 @@
package windviz
import (
"sync"
"time"
)
// Cache is a small bounded cache of rasterized fields keyed by request
// parameters and dataset epoch. It is safe for concurrent use.
//
// Visualization requests repeat heavily (a frontend re-fetches the same
// layer as users pan within a tile), so even a tiny cache removes most
// recomputation. Eviction is simplest-possible: when full, the whole map is
// cleared. Entries also expire after TTL.
type Cache struct {
mu sync.Mutex
entries map[string]cacheEntry
max int
ttl time.Duration
now func() time.Time
}
type cacheEntry struct {
field Field
expires time.Time
}
// NewCache returns a cache holding up to max entries for ttl each.
func NewCache(max int, ttl time.Duration) *Cache {
if max <= 0 {
max = 64
}
if ttl <= 0 {
ttl = 10 * time.Minute
}
return &Cache{
entries: make(map[string]cacheEntry, max),
max: max,
ttl: ttl,
now: time.Now,
}
}
// Get returns the cached field for key, if present and unexpired.
func (c *Cache) Get(key string) (Field, bool) {
c.mu.Lock()
defer c.mu.Unlock()
e, ok := c.entries[key]
if !ok || c.now().After(e.expires) {
return nil, false
}
return e.field, true
}
// Put stores field under key.
func (c *Cache) Put(key string, field Field) {
c.mu.Lock()
defer c.mu.Unlock()
if len(c.entries) >= c.max {
c.entries = make(map[string]cacheEntry, c.max)
}
c.entries[key] = cacheEntry{field: field, expires: c.now().Add(c.ttl)}
}