- Move ARCHITECTURE.md and CLAUDE.md to docs/ directory - Replace "real-time" terminology with accurate "low-latency" and "high-performance" - Update README to reflect correct performance characteristics - Add comprehensive ICAO country database with SQLite backend - Fix display options positioning and functionality - Add map scale controls and improved range ring visibility - Enhance aircraft marker orientation and trail management 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
119 lines
No EOL
2.7 KiB
Go
119 lines
No EOL
2.7 KiB
Go
package icao
|
|
|
|
import (
|
|
"database/sql"
|
|
"embed"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
//go:embed icao.db
|
|
var icaoFS embed.FS
|
|
|
|
// Database handles ICAO address to country lookups
|
|
type Database struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// CountryInfo represents country information for an aircraft
|
|
type CountryInfo struct {
|
|
Country string `json:"country"`
|
|
CountryCode string `json:"country_code"`
|
|
Flag string `json:"flag"`
|
|
}
|
|
|
|
// NewDatabase creates a new ICAO database connection
|
|
func NewDatabase() (*Database, error) {
|
|
// Extract embedded database to a temporary file
|
|
data, err := icaoFS.ReadFile("icao.db")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read embedded ICAO database: %w", err)
|
|
}
|
|
|
|
// Create temporary file for the database
|
|
tmpFile, err := os.CreateTemp("", "icao-*.db")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create temporary file: %w", err)
|
|
}
|
|
tmpPath := tmpFile.Name()
|
|
|
|
// Write database data to temporary file
|
|
if _, err := io.WriteString(tmpFile, string(data)); err != nil {
|
|
tmpFile.Close()
|
|
os.Remove(tmpPath)
|
|
return nil, fmt.Errorf("failed to write database to temp file: %w", err)
|
|
}
|
|
tmpFile.Close()
|
|
|
|
// Open SQLite database
|
|
db, err := sql.Open("sqlite3", tmpPath+"?mode=ro") // Read-only mode
|
|
if err != nil {
|
|
os.Remove(tmpPath)
|
|
return nil, fmt.Errorf("failed to open SQLite database: %w", err)
|
|
}
|
|
|
|
// Test the database connection
|
|
if err := db.Ping(); err != nil {
|
|
db.Close()
|
|
os.Remove(tmpPath)
|
|
return nil, fmt.Errorf("failed to ping database: %w", err)
|
|
}
|
|
|
|
return &Database{db: db}, nil
|
|
}
|
|
|
|
// LookupCountry returns country information for an ICAO address
|
|
func (d *Database) LookupCountry(icaoHex string) (*CountryInfo, error) {
|
|
if len(icaoHex) != 6 {
|
|
return &CountryInfo{
|
|
Country: "Unknown",
|
|
CountryCode: "XX",
|
|
Flag: "🏳️",
|
|
}, nil
|
|
}
|
|
|
|
// Convert hex string to integer
|
|
icaoInt, err := strconv.ParseInt(icaoHex, 16, 64)
|
|
if err != nil {
|
|
return &CountryInfo{
|
|
Country: "Unknown",
|
|
CountryCode: "XX",
|
|
Flag: "🏳️",
|
|
}, nil
|
|
}
|
|
|
|
var country, countryCode, flag string
|
|
query := `
|
|
SELECT country, country_code, flag
|
|
FROM icao_allocations
|
|
WHERE ? BETWEEN start_addr AND end_addr
|
|
LIMIT 1
|
|
`
|
|
|
|
err = d.db.QueryRow(query, icaoInt).Scan(&country, &countryCode, &flag)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return &CountryInfo{
|
|
Country: "Unknown",
|
|
CountryCode: "XX",
|
|
Flag: "🏳️",
|
|
}, nil
|
|
}
|
|
return nil, fmt.Errorf("database query failed: %w", err)
|
|
}
|
|
|
|
return &CountryInfo{
|
|
Country: country,
|
|
CountryCode: countryCode,
|
|
Flag: flag,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the database connection
|
|
func (d *Database) Close() error {
|
|
return d.db.Close()
|
|
} |