mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
Add "de_" variants of grid/lat/lon #42
This commit is contained in:
179
data/spot.py
179
data/spot.py
@@ -10,29 +10,26 @@ from pyhamtools.locator import locator_to_latlong, latlong_to_locator
|
||||
from core.constants import DXCC_FLAGS
|
||||
from core.lookup_helper import lookup_helper
|
||||
|
||||
|
||||
# Data class that defines a spot.
|
||||
@dataclass
|
||||
class Spot:
|
||||
# Unique identifier for the spot
|
||||
id: str = None
|
||||
|
||||
|
||||
# DX (spotted) operator info
|
||||
|
||||
# Callsign of the operator that has been spotted
|
||||
dx_call: str = None
|
||||
# Callsign of the operator that has spotted them
|
||||
de_call: str = None
|
||||
# Name of the operator that has been spotted
|
||||
dx_name: str = None
|
||||
# Country of the DX operator
|
||||
dx_country: str = None
|
||||
# Country of the spotter
|
||||
de_country: str = None
|
||||
# Country flag of the DX operator
|
||||
dx_flag: str = None
|
||||
# Country flag of the spotter
|
||||
de_flag: str = None
|
||||
# Continent of the DX operator
|
||||
dx_continent: str = None
|
||||
# Continent of the spotter
|
||||
de_continent: str = None
|
||||
# DXCC ID of the DX operator
|
||||
dx_dxcc_id: int = None
|
||||
# DXCC ID of the spotter
|
||||
@@ -44,6 +41,42 @@ class Spot:
|
||||
# If this is an APRS spot, what SSID was the DX operator using?
|
||||
# This is a string not an int for now, as I often see non-numeric ones somehow
|
||||
dx_aprs_ssid: str = None
|
||||
# Maidenhead grid locator for the DX. This could be from a geographical reference e.g. POTA, or just from the
|
||||
# country
|
||||
dx_grid: str = None
|
||||
# Latitude & longitude of the DX, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ
|
||||
# lookup
|
||||
dx_latitude: float = None
|
||||
dx_longitude: float = None
|
||||
# DX Location source. Indicates how accurate the location might be. Values: "SPOT", "QRZ, "DXCC", "NONE"
|
||||
dx_location_source: str = "NONE"
|
||||
# DX Location good. Indicates that the software thinks the location data is good enough to plot on a map.
|
||||
dx_location_good: bool = False
|
||||
|
||||
|
||||
# DE (Spotter) info
|
||||
|
||||
# Callsign of the spotter
|
||||
de_call: str = None
|
||||
# Country of the spotter
|
||||
de_country: str = None
|
||||
# Country flag of the spotter
|
||||
de_flag: str = None
|
||||
# Continent of the spotter
|
||||
de_continent: str = None
|
||||
# Maidenhead grid locator for the spotter. This is not going to be from a xOTA reference so it will likely just be
|
||||
# a QRZ or DXCC lookup. If the spotter is also portable, this is probably wrong, but it's good enough for some
|
||||
# simple mapping.
|
||||
de_grid: str = None
|
||||
# Latitude & longitude of the DX, in degrees. This is not going to be from a xOTA reference so it will likely just
|
||||
# be a QRZ or DXCC lookup. If the spotter is also portable, this is probably wrong, but it's good enough for some
|
||||
# simple mapping.
|
||||
de_latitude: float = None
|
||||
de_longitude: float = None
|
||||
|
||||
|
||||
# General QSO info
|
||||
|
||||
# Reported mode, such as SSB, PHONE, CW, FT8...
|
||||
mode: str = None
|
||||
# Inferred mode "family". One of "CW", "PHONE" or "DIGI".
|
||||
@@ -54,18 +87,14 @@ class Spot:
|
||||
freq: float = None
|
||||
# Band, defined by the frequency, e.g. "40m" or "70cm"
|
||||
band: str = None
|
||||
# Time of the spot, UTC seconds since UNIX epoch
|
||||
time: float = None
|
||||
# Time of the spot, ISO 8601
|
||||
time_iso: str = None
|
||||
# Time that this software received the spot, UTC seconds since UNIX epoch. This is used with the "since_received"
|
||||
# call to our API to receive all data that is new to us, even if by a quirk of the API it might be older than the
|
||||
# list time the client polled the API.
|
||||
received_time: float = None
|
||||
# Time that this software received the spot, ISO 8601
|
||||
received_time_iso: str = None
|
||||
# Comment left by the spotter, if any
|
||||
comment: str = None
|
||||
# QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.
|
||||
qrt: bool = False
|
||||
|
||||
|
||||
# Special Interest Group info
|
||||
|
||||
# Special Interest Group (SIG), e.g. outdoor activity programme such as POTA
|
||||
sig: str = None
|
||||
# SIG references. We allow multiple here for e.g. n-fer activations, unlike ADIF SIG_INFO
|
||||
@@ -76,17 +105,24 @@ class Spot:
|
||||
activation_score: int = None
|
||||
# Icon, from the Font Awesome set. This is fairly opinionated but is here to help the Spothole web UI and Field Spotter. Does not include the "fa-" prefix.
|
||||
icon: str = "question"
|
||||
# Maidenhead grid locator for the spot. This could be from a geographical reference e.g. POTA, or just from the country
|
||||
grid: str = None
|
||||
# Latitude & longitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup
|
||||
latitude: 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 = False
|
||||
# QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments.
|
||||
qrt: bool = False
|
||||
|
||||
|
||||
# Timing info
|
||||
|
||||
# Time of the spot, UTC seconds since UNIX epoch
|
||||
time: float = None
|
||||
# Time of the spot, ISO 8601
|
||||
time_iso: str = None
|
||||
# Time that this software received the spot, UTC seconds since UNIX epoch. This is used with the "since_received"
|
||||
# call to our API to receive all data that is new to us, even if by a quirk of the API it might be older than the
|
||||
# list time the client polled the API.
|
||||
received_time: float = None
|
||||
# Time that this software received the spot, ISO 8601
|
||||
received_time_iso: str = None
|
||||
|
||||
|
||||
# Source info
|
||||
|
||||
# Where we got the spot from, e.g. "POTA", "Cluster"...
|
||||
source: str = None
|
||||
# The ID the source gave it, if any.
|
||||
@@ -131,15 +167,17 @@ class Spot:
|
||||
if self.de_call and "-" in self.de_call:
|
||||
self.de_call = self.de_call.split("-")[0]
|
||||
|
||||
# Spotter country, continent, zones etc. from callsign
|
||||
if self.de_call and not self.de_country:
|
||||
self.de_country = lookup_helper.infer_country_from_callsign(self.de_call)
|
||||
if self.de_call and not self.de_continent:
|
||||
self.de_continent = lookup_helper.infer_continent_from_callsign(self.de_call)
|
||||
if self.de_call and not self.de_dxcc_id:
|
||||
self.de_dxcc_id = lookup_helper.infer_dxcc_id_from_callsign(self.de_call)
|
||||
if self.de_dxcc_id and self.de_dxcc_id in DXCC_FLAGS and not self.de_flag:
|
||||
self.de_flag = DXCC_FLAGS[self.de_dxcc_id]
|
||||
# Spotter country, continent, zones etc. from callsign.
|
||||
# DE of "RBNHOLE" and "SOTAMAT" are not things we can look up location for
|
||||
if self.de_call != "RBNHOLE" and self.de_call != "SOTAMAT":
|
||||
if self.de_call and not self.de_country:
|
||||
self.de_country = lookup_helper.infer_country_from_callsign(self.de_call)
|
||||
if self.de_call and not self.de_continent:
|
||||
self.de_continent = lookup_helper.infer_continent_from_callsign(self.de_call)
|
||||
if self.de_call and not self.de_dxcc_id:
|
||||
self.de_dxcc_id = lookup_helper.infer_dxcc_id_from_callsign(self.de_call)
|
||||
if self.de_dxcc_id and self.de_dxcc_id in DXCC_FLAGS and not self.de_flag:
|
||||
self.de_flag = DXCC_FLAGS[self.de_dxcc_id]
|
||||
|
||||
# Band from frequency
|
||||
if self.freq and not self.band:
|
||||
@@ -165,15 +203,15 @@ class Spot:
|
||||
if self.mode and not self.mode_type:
|
||||
self.mode_type = lookup_helper.infer_mode_type_from_mode(self.mode)
|
||||
|
||||
# Grid to lat/lon and vice versa
|
||||
if self.grid and not self.latitude:
|
||||
ll = locator_to_latlong(self.grid)
|
||||
self.latitude = ll[0]
|
||||
self.longitude = ll[1]
|
||||
if self.latitude and self.longitude and not self.grid:
|
||||
self.grid = latlong_to_locator(self.latitude, self.longitude, 8)
|
||||
if self.latitude:
|
||||
self.location_source = "SPOT"
|
||||
# DX Grid to lat/lon and vice versa
|
||||
if self.dx_grid and not self.dx_latitude:
|
||||
ll = locator_to_latlong(self.dx_grid)
|
||||
self.dx_latitude = ll[0]
|
||||
self.dx_longitude = ll[1]
|
||||
if self.dx_latitude and self.dx_longitude and not self.dx_grid:
|
||||
self.dx_grid = latlong_to_locator(self.dx_latitude, self.dx_longitude, 8)
|
||||
if self.dx_latitude:
|
||||
self.dx_location_source = "SPOT"
|
||||
|
||||
# QRT comment detection
|
||||
if self.comment and not self.qrt:
|
||||
@@ -184,26 +222,45 @@ class Spot:
|
||||
# the one from the park reference they're at.
|
||||
if self.dx_call and not self.dx_name:
|
||||
self.dx_name = lookup_helper.infer_name_from_callsign(self.dx_call)
|
||||
if self.dx_call and not self.latitude:
|
||||
if self.dx_call and not self.dx_latitude:
|
||||
latlon = lookup_helper.infer_latlon_from_callsign_qrz(self.dx_call)
|
||||
if latlon:
|
||||
self.latitude = latlon[0]
|
||||
self.longitude = latlon[1]
|
||||
self.grid = lookup_helper.infer_grid_from_callsign_qrz(self.dx_call)
|
||||
self.location_source = "QRZ"
|
||||
self.dx_latitude = latlon[0]
|
||||
self.dx_longitude = latlon[1]
|
||||
self.dx_grid = lookup_helper.infer_grid_from_callsign_qrz(self.dx_call)
|
||||
self.dx_location_source = "QRZ"
|
||||
|
||||
# Last resort for getting a position, use the DXCC entity.
|
||||
if self.dx_call and not self.latitude:
|
||||
# Last resort for getting a DX position, use the DXCC entity.
|
||||
if self.dx_call and not self.dx_latitude:
|
||||
latlon = lookup_helper.infer_latlon_from_callsign_dxcc(self.dx_call)
|
||||
if latlon:
|
||||
self.latitude = latlon[0]
|
||||
self.longitude = latlon[1]
|
||||
self.grid = lookup_helper.infer_grid_from_callsign_dxcc(self.dx_call)
|
||||
self.location_source = "DXCC"
|
||||
self.dx_latitude = latlon[0]
|
||||
self.dx_longitude = latlon[1]
|
||||
self.dx_grid = lookup_helper.infer_grid_from_callsign_dxcc(self.dx_call)
|
||||
self.dx_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
|
||||
# DX 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)
|
||||
self.dx_location_good = self.dx_location_source == "SPOT" or (
|
||||
self.dx_location_source == "QRZ" and not "/" in self.dx_call)
|
||||
|
||||
# DE of "RBNHOLE" and "SOTAMAT" are not things we can look up location for
|
||||
if self.de_call != "RBNHOLE" and self.de_call != "SOTAMAT":
|
||||
# DE operator position lookup, using QRZ.com.
|
||||
if self.de_call and not self.de_latitude:
|
||||
latlon = lookup_helper.infer_latlon_from_callsign_qrz(self.de_call)
|
||||
if latlon:
|
||||
self.de_latitude = latlon[0]
|
||||
self.de_longitude = latlon[1]
|
||||
self.de_grid = lookup_helper.infer_grid_from_callsign_qrz(self.de_call)
|
||||
|
||||
# Last resort for getting a DE position, use the DXCC entity.
|
||||
if self.de_call and not self.de_latitude:
|
||||
latlon = lookup_helper.infer_latlon_from_callsign_dxcc(self.de_call)
|
||||
if latlon:
|
||||
self.de_latitude = latlon[0]
|
||||
self.de_longitude = latlon[1]
|
||||
self.de_grid = lookup_helper.infer_grid_from_callsign_dxcc(self.de_call)
|
||||
|
||||
# Always create an ID based on a hash of every parameter *except* received_time. This is used as the index
|
||||
# to a map, which as a byproduct avoids us having multiple duplicate copies of the object that are identical
|
||||
@@ -217,4 +274,4 @@ class Spot:
|
||||
|
||||
# JSON serialise
|
||||
def to_json(self):
|
||||
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
|
||||
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
|
||||
|
||||
Reference in New Issue
Block a user