Files
beacon-server/internal/config/foreign_borders.go
T
MrAlders0n 0e242574fb feat(meshmapper): import region boundaries from the Zones API
Imported boundaries override borderFile on the border map and for foreign marking, falling back to the manual border when MeshMapper has none.
2026-09-30 12:38:36 -04:00

85 lines
2.2 KiB
Go

// Copyright 2026 Beacon Contributors
// SPDX-License-Identifier: AGPL-3.0-or-later
package config
import (
"encoding/json"
"fmt"
"maps"
"os"
"slices"
"github.com/MeshCore-Beacon/beacon-server/internal/borders"
"github.com/paulmach/orb"
"github.com/paulmach/orb/geojson"
)
func (c *Config) hasBorderFile() bool {
for _, details := range c.IATAs {
if details.BorderFile != "" {
return true
}
}
return false
}
// LoadLocalBorders reads the validated border files used for foreign marking,
// keyed by IATA. IATA airport coordinates are not boundaries. Nil when disabled.
func LoadLocalBorders(cfg *Config) (map[string]json.RawMessage, error) {
if !cfg.Nodes.MarkForeign {
return nil, nil
}
files := map[string]json.RawMessage{}
for code, details := range cfg.IATAs {
if details.BorderFile == "" {
continue
}
raw, err := os.ReadFile(details.BorderFile)
if err != nil {
return nil, fmt.Errorf("nodes.mark_foreign: reading border for %s: %w", code, err)
}
validated, err := ValidateBorder(raw)
if err != nil {
return nil, fmt.Errorf("nodes.mark_foreign: invalid border for %s: %w", code, err)
}
files[code] = validated
}
// Fail at startup rather than on the first rebuild.
if _, err := BuildLocalBorders(files, nil); err != nil {
return nil, err
}
return files, nil
}
// BuildLocalBorders unions validated borders; an imported boundary replaces
// the same IATA's file. Nil when there are none.
func BuildLocalBorders(files, imported map[string]json.RawMessage) (*borders.Local, error) {
merged := maps.Clone(files)
if merged == nil {
merged = map[string]json.RawMessage{}
}
maps.Copy(merged, imported)
if len(merged) == 0 {
return nil, nil
}
var polygons []orb.Polygon
for _, code := range slices.Sorted(maps.Keys(merged)) {
feature, err := geojson.UnmarshalFeature(merged[code])
if err != nil {
return nil, fmt.Errorf("nodes.mark_foreign: decoding border for %s: %w", code, err)
}
switch geometry := feature.Geometry.(type) {
case orb.Polygon:
polygons = append(polygons, geometry)
case orb.MultiPolygon:
polygons = append(polygons, geometry...)
}
}
local, err := borders.New(polygons)
if err != nil {
return nil, fmt.Errorf("nodes.mark_foreign: %w", err)
}
return local, nil
}