fix(counties): make the cluster and the entry panel agree on a US county
An operator asked why K1SEI showed "Middlesex" in Info (F2) and "Lower
Connecticut River Valley" in the cluster. Two sources: the entry panel has
a callbook answer, a spot carries only a callsign so the cluster derives one
from the FCC licence ZIP through GeoNames — and GeoNames has followed the
Census in replacing Connecticut's counties with the 2022 planning regions.
No award, callbook or log uses those, so every CT station matched nothing:
new county for ever, and counting toward nothing.
Measured against a full ULS import (1 556 444 US callsigns), 23 223 resolved
to a name the USA-CA reference does not contain. Three causes, three fixes:
- Spelling. "City and County of San Francisco", "Baltimore (city)",
"Nome (CA)", plus counties renamed since the award list was drawn
(Kusilvak, Oglala Lakota, the Valdez-Cordova split) and Alaska's four
"X City and Borough", whose reference codes read "JUNEAUCITYAND" because
the county-type suffix strip eats the wrong end. Normalised in
award.USCountyKey, which both sides already go through. 7 515 callsigns,
no re-download needed.
- Doña Ana, NM shipped into the reference as "NM/DO̱AANA" — mangled by a
non-UTF-8 CSV line, a code nothing could ever produce, so that county was
unwinnable and silent about it. Row repaired, cntygen now refuses such a
line, and a test makes every one of the 3 102 references reproduce its own
code from its own name.
- Connecticut. A planning region is drawn from towns in several counties, so
no name maps to a name — only the ZIP can resolve it. cmd/ctzipgen builds
the table from the Census 2020 crosswalk, filling PO-box-only ZIPs from the
nearest resolved centroid; all 11 ZIPs GeoNames still labels with a real
county agree with the result. 15 037 callsigns, applied at import, so the
store now carries a rules version and Settings says when a re-download is
needed.
Alignment itself is the last piece: a spot now shows the county the station is
logged with when we have one, and falls back to the ZIP-derived county only for
stations never worked.
This commit is contained in:
@@ -10495,6 +10495,11 @@ func (a *App) applyULSCounty(q *qso.QSO) {
|
||||
type ULSStatusResult struct {
|
||||
Count int `json:"count"`
|
||||
UpdatedAt string `json:"updated_at"` // RFC3339, empty if never downloaded
|
||||
// NeedsRefresh is set when the stored data predates a correction to how a
|
||||
// callsign's county is derived. The download date alone cannot show this —
|
||||
// a database fetched yesterday by an older OpsLog still holds the wrong
|
||||
// Connecticut counties — so it is reported as its own flag.
|
||||
NeedsRefresh bool `json:"needs_refresh"`
|
||||
}
|
||||
|
||||
// ULSStatus returns the state of the offline US county database.
|
||||
@@ -10506,7 +10511,7 @@ func (a *App) ULSStatus() ULSStatusResult {
|
||||
if t := a.uls.UpdatedAt(); !t.IsZero() {
|
||||
updated = t.Format(time.RFC3339)
|
||||
}
|
||||
return ULSStatusResult{Count: a.uls.Count(), UpdatedAt: updated}
|
||||
return ULSStatusResult{Count: a.uls.Count(), UpdatedAt: updated, NeedsRefresh: a.uls.NeedsRefresh()}
|
||||
}
|
||||
|
||||
// DownloadULSCounties downloads and (re)builds the offline US county database in
|
||||
@@ -17124,7 +17129,11 @@ type clusterStatusCache struct {
|
||||
workedCalls map[string]struct{}
|
||||
workedCallSlots map[string]struct{} // nil unless the "same slot" option is on
|
||||
workedCounties map[string]struct{}
|
||||
workedPOTA map[string]struct{}
|
||||
// callCounties holds callsign → "STATE,County" for stations already logged
|
||||
// with a county, so a spot shows the county the entry panel showed rather
|
||||
// than the one derived from the licence ZIP. See qso.CallCounties.
|
||||
callCounties map[string]string
|
||||
workedPOTA map[string]struct{}
|
||||
workedPfx map[string]struct{}
|
||||
workedGrids map[string]struct{} // "GRID|MODE", mode normalised like the rest
|
||||
normMode func(string) string // nil unless digital-mode grouping is on
|
||||
@@ -17192,6 +17201,7 @@ func (a *App) clusterStatusMaps() *clusterStatusCache {
|
||||
// Orthogonal dimensions: worked US counties (for the ULS callsign→county
|
||||
// lookup) and worked POTA parks.
|
||||
c.workedCounties, _ = a.qso.WorkedCountyKeys(a.ctx, award.USCountyKey)
|
||||
c.callCounties, _ = a.qso.CallCounties(a.ctx)
|
||||
c.workedPOTA, _ = a.qso.WorkedPOTARefs(a.ctx)
|
||||
// One more DISTINCT scan when the snapshot is rebuilt, then pure map lookups
|
||||
// per spot — the same shape as the county and POTA sets beside it, which is
|
||||
@@ -17468,9 +17478,27 @@ func (a *App) ClusterSpotStatuses(spots []SpotQuery) []SpotStatus {
|
||||
}
|
||||
}
|
||||
}
|
||||
// NEW COUNTY: resolve the callsign's home county from the offline ULS
|
||||
// store (US only; inert until downloaded) and flag if never worked.
|
||||
if a.uls != nil {
|
||||
// NEW COUNTY. Two sources, better one first:
|
||||
//
|
||||
// 1. the county this station was logged with — a callbook's answer,
|
||||
// the same one the entry panel shows;
|
||||
// 2. the offline ULS store, which derives a county from the licence
|
||||
// ZIP (US only; inert until downloaded, ~98% for fixed stations).
|
||||
//
|
||||
// Preferring the log is what keeps the cluster column and the entry
|
||||
// panel from disagreeing about the same callsign, which was impossible
|
||||
// to explain and made both look wrong.
|
||||
if cnty, ok := idx.callCounties[q.Call]; ok {
|
||||
st, name, _ := strings.Cut(cnty, ",")
|
||||
out[i].State, out[i].County = st, name
|
||||
// Logged means worked, so this can never be a new county — but say
|
||||
// so through the same key the flag below uses, not by assumption.
|
||||
if key := award.USCountyKey(st, name); key != "" {
|
||||
if _, done := workedCounties[key]; !done {
|
||||
out[i].NewCounty = true
|
||||
}
|
||||
}
|
||||
} else if a.uls != nil {
|
||||
if loc, ok := a.uls.Resolve(q.Call); ok {
|
||||
out[i].County, out[i].State = loc.County, loc.State
|
||||
if key := award.USCountyKey(loc.State, loc.County); key != "" {
|
||||
|
||||
Reference in New Issue
Block a user