119 lines
3.7 KiB
Go
119 lines
3.7 KiB
Go
package dxcc
|
|
|
|
import "strings"
|
|
|
|
// The dxccByName table itself lives in dxcc_names_gen.go (generated by joining
|
|
// cty.dat to the authoritative ARRL/ADIF entity list). cty.dat doesn't carry
|
|
// the ADIF DXCC number, so we map its entity names → numbers here to stamp
|
|
// MY_DXCC / DXCC at log time without a network round-trip.
|
|
|
|
// EntityDXCC returns the ADIF DXCC entity number for the given cty.dat entity
|
|
// name, or 0 when unknown (callers should then leave the field empty rather
|
|
// than guess). Case-insensitive and whitespace-tolerant.
|
|
func EntityDXCC(name string) int {
|
|
if name == "" {
|
|
return 0
|
|
}
|
|
// Fast path: exact (lower-cased) match against the cty.dat names.
|
|
if n := dxccByName[strings.ToLower(strings.TrimSpace(name))]; n != 0 {
|
|
return n
|
|
}
|
|
// Fallback: canonicalise so abbreviation/spelling differences still match
|
|
// (e.g. an ADIF import that wrote "Lord Howe I." instead of cty.dat's
|
|
// "Lord Howe Island").
|
|
if n := dxccByCanon[canonEntity(name)]; n != 0 {
|
|
return n
|
|
}
|
|
// Last resort: cty.dat pseudo-entities (Sicily, African Italy) report a
|
|
// parent DXCC entity for the number.
|
|
if c := CanonicalEntityName(name); !strings.EqualFold(c, name) {
|
|
return dxccByName[strings.ToLower(strings.TrimSpace(c))]
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// nameByDXCC reverses dxccByName (number → a representative entity name),
|
|
// built once. When several names share a number, the longest (usually the most
|
|
// complete) wins. Names are Title-cased for display.
|
|
var nameByDXCC = func() map[int]string {
|
|
m := make(map[int]string, len(dxccByName))
|
|
for name, num := range dxccByName {
|
|
if cur, ok := m[num]; !ok || len(name) > len(cur) {
|
|
m[num] = name
|
|
}
|
|
}
|
|
return m
|
|
}()
|
|
|
|
// NameForDXCC returns a display name for an ADIF DXCC entity number, or "" if
|
|
// unknown.
|
|
func NameForDXCC(n int) string {
|
|
name, ok := nameByDXCC[n]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return strings.Title(name) //nolint:staticcheck // ASCII entity names
|
|
}
|
|
|
|
// dxccByCanon is dxccByName re-keyed by the canonical entity form, built once.
|
|
var dxccByCanon = func() map[string]int {
|
|
m := make(map[string]int, len(dxccByName))
|
|
for name, num := range dxccByName {
|
|
m[canonEntity(name)] = num
|
|
}
|
|
return m
|
|
}()
|
|
|
|
// canonEntity reduces an entity name to a canonical token stream, expanding the
|
|
// common abbreviations that differ between naming conventions and normalising
|
|
// punctuation / "&".
|
|
func canonEntity(s string) string {
|
|
s = strings.ToLower(strings.TrimSpace(s))
|
|
s = strings.ReplaceAll(s, "&", " and ")
|
|
fields := strings.FieldsFunc(s, func(r rune) bool {
|
|
switch r {
|
|
case ' ', '.', ',', '-', '\'', '(', ')', '/':
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
for i, w := range fields {
|
|
switch w {
|
|
case "i":
|
|
fields[i] = "island"
|
|
case "is":
|
|
fields[i] = "islands"
|
|
case "st":
|
|
fields[i] = "saint"
|
|
case "mt":
|
|
fields[i] = "mount"
|
|
case "rep":
|
|
fields[i] = "republic"
|
|
case "dem":
|
|
fields[i] = "democratic"
|
|
case "fed":
|
|
fields[i] = "federal"
|
|
}
|
|
}
|
|
return strings.Join(fields, " ")
|
|
}
|
|
|
|
// ctyEntityAliases maps cty.dat's non-DXCC pseudo-entities — the CQ-zone /
|
|
// WAE splits AD1C lists separately — onto the ARRL DXCC entity they belong
|
|
// to. cty.dat reports e.g. "Sicily" so contesters get the right zones, but
|
|
// for DXCC (and the COUNTRY field) they are Italy.
|
|
var ctyEntityAliases = map[string]string{
|
|
"sicily": "Italy",
|
|
"african italy": "Italy",
|
|
// NB: Sardinia is intentionally absent — it's a real DXCC entity.
|
|
}
|
|
|
|
// CanonicalEntityName normalises a cty.dat entity name to its ARRL DXCC
|
|
// entity. Names that already are DXCC entities pass through unchanged.
|
|
func CanonicalEntityName(name string) string {
|
|
if c, ok := ctyEntityAliases[strings.ToLower(strings.TrimSpace(name))]; ok {
|
|
return c
|
|
}
|
|
return name
|
|
}
|