Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions internal/engine/baker/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package baker

import (
"sort"
"strconv"
)

// CellMeta is the per-cell metadata extracted at import time for the chart
// library to display. It comes from the cell's S-57 header (DSID/DSPM) plus its
// M_COVR coverage — gathered with the same cheap coverage-only parse the bake's
// pass 1 uses, so it adds no full re-parse. Title is left to the caller to fill
// from the exchange-set catalogue (CATALOG.031 LFIL), since S-57 headers carry
// no human chart name.
type CellMeta struct {
Name string `json:"name"` // cell stem, e.g. "US5MD1MC"
Title string `json:"title,omitempty"` // human chart name (from CATALOG.031 LFIL); empty if none — consumers show Name
Scale int `json:"scale,omitempty"` // compilation scale denominator (CSCL)
Edition string `json:"edition,omitempty"`
Update string `json:"update,omitempty"`
IssueDate string `json:"issueDate,omitempty"` // YYYYMMDD
Agency int `json:"agency,omitempty"` // IHO producing-agency code (550 = NOAA)
BBox [4]float64 `json:"bbox,omitempty"` // [west, south, east, north]
HasBBox bool `json:"-"`
}

// ExtractCellMeta parses each cell's header + coverage (coverage-only, cheap) and
// returns per-cell metadata keyed by cell stem. Cells that fail to parse are
// reported via onSkip and omitted. Title is left empty (S-57 headers carry no human
// chart name — only the cell code); the caller overlays the CATALOG.031 long name
// where the exchange set provides one.
func ExtractCellMeta(cells map[string]CellData, onSkip func(name string, err error)) map[string]CellMeta {
out := make(map[string]CellMeta, len(cells))
names := make([]string, 0, len(cells))
for n := range cells {
names = append(names, n)
}
sort.Strings(names)
for _, name := range names {
cd := cells[name]
chart, err := ParseCellCoverage(name, cd.Base, cd.Updates)
if err != nil {
if onSkip != nil {
onSkip(name, err)
}
continue
}
stem := cellStem(chart.DatasetName())
if stem == "" {
stem = cellStem(name)
}
m := CellMeta{
Name: stem,
Scale: int(chart.CompilationScale()),
Edition: chart.Edition(),
Update: chart.UpdateNumber(),
IssueDate: chart.IssueDate(),
Agency: chart.ProducingAgency(),
}
b := chart.Bounds()
if b.MaxLon > b.MinLon && b.MaxLat > b.MinLat {
m.BBox = [4]float64{b.MinLon, b.MinLat, b.MaxLon, b.MaxLat}
m.HasBBox = true
}
out[stem] = m
}
return out
}

// cellStem trims a trailing ".000"/".NNN" or directory path from a cell name.
func cellStem(name string) string {
// Strip any directory.
for i := len(name) - 1; i >= 0; i-- {
if name[i] == '/' || name[i] == '\\' {
name = name[i+1:]
break
}
}
// Strip a 3-digit S-57 extension.
if n := len(name); n >= 4 && name[n-4] == '.' {
if _, err := strconv.Atoi(name[n-3:]); err == nil {
return name[:n-4]
}
}
return name
}
42 changes: 42 additions & 0 deletions internal/engine/baker/meta_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package baker

import (
"os"
"testing"
)

func TestExtractCellMeta(t *testing.T) {
data, err := os.ReadFile("../../../testdata/US5MD1MC.000")
if err != nil {
t.Fatal(err)
}
meta := ExtractCellMeta(map[string]CellData{"US5MD1MC.000": {Base: data}}, nil)
m, ok := meta["US5MD1MC"]
if !ok {
t.Fatalf("no metadata for US5MD1MC; got keys %v", keys(meta))
}
if m.Scale != 12000 {
t.Errorf("Scale = %d, want 12000", m.Scale)
}
if m.Agency != 550 { // 550 = NOAA (US)
t.Errorf("Agency = %d, want 550 (NOAA)", m.Agency)
}
if m.IssueDate == "" {
t.Error("expected an issue date")
}
if !m.HasBBox {
t.Error("expected a coverage bbox")
}
// Annapolis Harbor is around 38.9–39.0 N, -76.5–-76.4 W.
if m.BBox[0] < -77 || m.BBox[0] > -76 || m.BBox[3] < 38 || m.BBox[3] > 40 {
t.Errorf("bbox looks wrong: %v", m.BBox)
}
}

func keys(m map[string]CellMeta) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}
2 changes: 2 additions & 0 deletions internal/engine/server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ func (s *Server) handleAPI(w http.ResponseWriter, r *http.Request) {
s.handleImport(w, r) // POST: server-side native bake → register a tile set; status polling
case r.URL.Path == "/api/packs":
s.handlePacks(w, r) // GET: all baked packs + enabled state
case strings.HasPrefix(r.URL.Path, "/api/pack/"):
s.handlePackDetail(w, r) // GET: one pack's full extracted metadata (per-cell)
case r.URL.Path == "/api/set/enable" || r.URL.Path == "/api/set/disable":
s.handleSetEnabled(w, r) // POST: show/hide a pack on the map (data kept)
case r.URL.Path == "/api/set":
Expand Down
Loading