Check pyhamtools callinfo for lat/Lon of DXCC from static country info as a last resort. Closes #13

This commit is contained in:
Ian Renton
2025-09-29 20:19:01 +01:00
parent cd575e7ed2
commit 60314c7d44
3 changed files with 53 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ import logging
from datetime import datetime from datetime import datetime
from pyhamtools import LookupLib, Callinfo from pyhamtools import LookupLib, Callinfo
from pyhamtools.locator import latlong_to_locator
from core.config import config from core.config import config
from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES from core.constants import BANDS, UNKNOWN_BAND, CW_MODES, PHONE_MODES, DATA_MODES, ALL_MODES
@@ -105,21 +106,34 @@ def infer_name_from_callsign(call):
return None return None
# Infer a latitude and longitude from a callsign (requires QRZ.com) # Infer a latitude and longitude from a callsign (requires QRZ.com)
def infer_latlon_from_callsign(call): def infer_latlon_from_callsign_qrz(call):
data = get_qrz_data_for_callsign(call) data = get_qrz_data_for_callsign(call)
if data and "latitude" in data and "longitude" in data: if data and "latitude" in data and "longitude" in data:
return [data["longitude"], data["longitude"]] return [data["latitude"], data["longitude"]]
else: else:
return None return None
# Infer a grid locator from a callsign (requires QRZ.com) # Infer a grid locator from a callsign (requires QRZ.com)
def infer_grid_from_callsign(call): def infer_grid_from_callsign_qrz(call):
data = get_qrz_data_for_callsign(call) data = get_qrz_data_for_callsign(call)
if data and "locator" in data: if data and "locator" in data:
return data["locator"] return data["locator"]
else: else:
return None return None
# Infer a latitude and longitude from a callsign (using DXCC, probably very inaccurate)
def infer_latlon_from_callsign_dxcc(call):
data = CALL_INFO_BASIC.get_lat_long(call)
if data and "latitude" in data and "longitude" in data:
return [data["latitude"], data["longitude"]]
else:
return None
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
def infer_grid_from_callsign_dxcc(call):
latlon = infer_latlon_from_callsign_dxcc(call)
return latlong_to_locator(latlon[0], latlon[1], 8)
# Convert objects to serialisable things. Used by JSON serialiser as a default when it encounters unserializable things. # Convert objects to serialisable things. Used by JSON serialiser as a default when it encounters unserializable things.
# Converts datetimes to ISO. # Converts datetimes to ISO.

View File

@@ -8,7 +8,8 @@ from pyhamtools.locator import locator_to_latlong, latlong_to_locator
from core.constants import DXCC_FLAGS from core.constants import DXCC_FLAGS
from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \ from core.utils import infer_mode_family_from_mode, infer_band_from_freq, infer_continent_from_callsign, \
infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign, \ infer_country_from_callsign, infer_cq_zone_from_callsign, infer_itu_zone_from_callsign, infer_dxcc_id_from_callsign, \
infer_mode_from_comment, infer_name_from_callsign, infer_latlon_from_callsign, infer_grid_from_callsign infer_mode_from_comment, infer_name_from_callsign, infer_latlon_from_callsign_dxcc, infer_grid_from_callsign_dxcc, \
infer_latlon_from_callsign_qrz, infer_grid_from_callsign_qrz
# Data class that defines a spot. # Data class that defines a spot.
@@ -76,6 +77,10 @@ class Spot:
# Latitude & longitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup # Latitude & longitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup
latitude: float = None latitude: float = None
longitude: float = None longitude: float = None
# Location source. Indicates how accurate the location might be. Values: "SPOT", "QRZ, "DXCC", "NONE"
location_source: str = None
# Location good. Indicates that the software thinks the location data is good enough to plot on a map.
location_good: bool = None
# QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments. # QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.
qrt: bool = None qrt: bool = None
# Where we got the spot from, e.g. "POTA", "Cluster"... # Where we got the spot from, e.g. "POTA", "Cluster"...
@@ -129,6 +134,8 @@ class Spot:
self.longitude = ll[1] self.longitude = ll[1]
if self.latitude and self.longitude and not self.grid: if self.latitude and self.longitude and not self.grid:
self.grid = latlong_to_locator(self.latitude, self.longitude, 8) self.grid = latlong_to_locator(self.latitude, self.longitude, 8)
if self.latitude:
self.location_source = "SPOT"
# QRT comment detection # QRT comment detection
if self.comment and not self.qrt: if self.comment and not self.qrt:
@@ -140,12 +147,24 @@ class Spot:
if self.dx_call and not self.dx_name: if self.dx_call and not self.dx_name:
self.dx_name = infer_name_from_callsign(self.dx_call) self.dx_name = infer_name_from_callsign(self.dx_call)
if self.dx_call and not self.latitude: if self.dx_call and not self.latitude:
latlon = infer_latlon_from_callsign(self.dx_call) latlon = infer_latlon_from_callsign_qrz(self.dx_call)
if latlon: if latlon:
self.latitude = latlon[0] self.latitude = latlon[0]
self.longitude = latlon[1] self.longitude = latlon[1]
if self.dx_call and not self.grid: self.grid = infer_grid_from_callsign_qrz(self.dx_call)
self.grid = infer_grid_from_callsign(self.dx_call) self.location_source = "QRZ"
# Last resort for getting a position, use the DXCC entity.
if self.dx_call and not self.latitude:
latlon = infer_latlon_from_callsign_dxcc(self.dx_call)
self.latitude = latlon[0]
self.longitude = latlon[1]
self.grid = infer_grid_from_callsign_dxcc(self.dx_call)
self.location_source = "DXCC"
# Location is "good" if it is from a spot, or from QRZ if the callsign doesn't contain a slash, so the operator
# is likely at home.
self.location_good = self.location_source == "SPOT" or (self.location_source == "QRZ" and not "/" in self.dx_call)
# JSON serialise # JSON serialise
def to_json(self): def to_json(self):

View File

@@ -417,6 +417,19 @@ components:
type: number type: number
description: Latitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup description: Latitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup
example: -1.2345 example: -1.2345
location_source:
type: string
description: Where we got the location (grid/latitude/longitude) from. If this was from the spot itself, it's likely quite accurate, but if we had to fall back to QRZ lookup, or even a location based on the DXCC itself, it will be a lot less accurate.
enum:
- SPOT
- QRZ
- DXCC
- NONE
example: SPOT
location_good:
type: boolean
description: Does the software think the location is good enough to put a marker on a map? This is true if the source is "SPOT", or alternatively if the source is "QRZ" and the callsign doesn't have a slash in it (i.e. operator likely at home).
example: true
qrt: qrt:
type: boolean type: boolean
description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments. description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.