From 9594040ea49fffb583d7a415631439fc30cfa3d1 Mon Sep 17 00:00:00 2001 From: Ian Renton Date: Thu, 16 Oct 2025 20:58:40 +0100 Subject: [PATCH] Add "de_" variants of grid/lat/lon #42 --- core/constants.py | 2 +- data/spot.py | 179 ++++++++++++++++++++++------------ spotproviders/aprsis.py | 4 +- spotproviders/gma.py | 4 +- spotproviders/hema.py | 4 +- spotproviders/parksnpeaks.py | 10 +- spotproviders/pota.py | 6 +- spotproviders/sota.py | 6 +- spotproviders/wwbota.py | 32 +++--- spotproviders/wwff.py | 4 +- webassets/apidocs/openapi.yml | 118 ++++++++++++---------- webassets/js/spots.js | 8 +- 12 files changed, 223 insertions(+), 154 deletions(-) diff --git a/core/constants.py b/core/constants.py index e4ae1ab..8b7f964 100644 --- a/core/constants.py +++ b/core/constants.py @@ -159,7 +159,7 @@ DXCC_FLAGS = { 107: "\U0001F1EC\U0001F1F3", # GUINEA 108: "\U0001F1E7\U0001F1F7", # BRAZIL 109: "\U0001F1EC\U0001F1FC", # GUINEA-BISSAU - 110: "", # HAWAII + 110: "\U0001F1FA\U0001F1F8", # HAWAII 111: "\U0001F1ED\U0001F1F2", # HEARD ISLAND 112: "\U0001F1E8\U0001F1F1", # CHILE 113: "", # IFNI diff --git a/data/spot.py b/data/spot.py index 0cf8b34..0b3c4ee 100644 --- a/data/spot.py +++ b/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) \ No newline at end of file + return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True) diff --git a/spotproviders/aprsis.py b/spotproviders/aprsis.py index dd75b56..5581088 100644 --- a/spotproviders/aprsis.py +++ b/spotproviders/aprsis.py @@ -45,8 +45,8 @@ class APRSIS(SpotProvider): dx_aprs_ssid=dx_aprs_ssid, de_call=data["via"], comment=data["comment"] if "comment" in data else None, - latitude=data["latitude"] if "latitude" in data else None, - longitude=data["longitude"] if "longitude" in data else None, + dx_latitude=data["latitude"] if "latitude" in data else None, + dx_longitude=data["longitude"] if "longitude" in data else None, icon="tower-cell", time=datetime.now(pytz.UTC).timestamp()) # APRS-IS spots are live so we can assume spot time is "now" diff --git a/spotproviders/gma.py b/spotproviders/gma.py index ea5fc4a..f306972 100644 --- a/spotproviders/gma.py +++ b/spotproviders/gma.py @@ -38,9 +38,9 @@ class GMA(HTTPSpotProvider): sig_refs_names=[source_spot["NAME"]], time=datetime.strptime(source_spot["DATE"] + source_spot["TIME"], "%Y%m%d%H%M").replace( tzinfo=pytz.UTC).timestamp(), - latitude=float(source_spot["LAT"]) if (source_spot["LAT"] and source_spot["LAT"] != "") else None, + dx_latitude=float(source_spot["LAT"]) if (source_spot["LAT"] and source_spot["LAT"] != "") else None, # Seen GMA spots with no (or empty) lat/lon - longitude=float(source_spot["LON"]) if (source_spot["LON"] and source_spot["LON"] != "") else None) + dx_longitude=float(source_spot["LON"]) if (source_spot["LON"] and source_spot["LON"] != "") else None) # GMA doesn't give what programme (SIG) the reference is for until we separately look it up. ref_response = self.REF_INFO_CACHE.get(self.REF_INFO_URL_ROOT + source_spot["REF"], diff --git a/spotproviders/hema.py b/spotproviders/hema.py index 26b148c..3355a1c 100644 --- a/spotproviders/hema.py +++ b/spotproviders/hema.py @@ -56,8 +56,8 @@ class HEMA(HTTPSpotProvider): sig_refs_names=[spot_items[4]], icon="mound", time=datetime.strptime(spot_items[0], "%d/%m/%Y %H:%M").replace(tzinfo=pytz.UTC).timestamp(), - latitude=float(spot_items[7]), - longitude=float(spot_items[8])) + dx_latitude=float(spot_items[7]), + dx_longitude=float(spot_items[8])) # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do # that for us. diff --git a/spotproviders/parksnpeaks.py b/spotproviders/parksnpeaks.py index 321f8af..5348ea1 100644 --- a/spotproviders/parksnpeaks.py +++ b/spotproviders/parksnpeaks.py @@ -63,9 +63,9 @@ class ParksNPeaks(HTTPSpotProvider): siota_dr = csv.DictReader(siota_csv_data.content.decode().splitlines()) for row in siota_dr: if row["SILO_CODE"] == spot.sig_refs[0]: - spot.latitude = float(row["LAT"]) - spot.longitude = float(row["LON"]) - spot.grid = row["LOCATOR"] + spot.dx_latitude = float(row["LAT"]) + spot.dx_longitude = float(row["LON"]) + spot.dx_grid = row["LOCATOR"] break # ZLOTA name/lat/lon lookup @@ -74,8 +74,8 @@ class ParksNPeaks(HTTPSpotProvider): for asset in zlota_data: if asset["code"] == spot.sig_refs[0]: spot.sig_refs_names = [asset["name"]] - spot.latitude = asset["y"] - spot.longitude = asset["x"] + spot.dx_latitude = asset["y"] + spot.dx_longitude = asset["x"] # Junk the "DE call", PNP always returns "ZLOTA" as the spotter for ZLOTA spots spot.de_call = None break diff --git a/spotproviders/pota.py b/spotproviders/pota.py index 5a59b95..ac2a90a 100644 --- a/spotproviders/pota.py +++ b/spotproviders/pota.py @@ -31,9 +31,9 @@ class POTA(HTTPSpotProvider): sig_refs_names=[source_spot["name"]], icon="tree", time=datetime.strptime(source_spot["spotTime"], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=pytz.UTC).timestamp(), - grid=source_spot["grid6"], - latitude=source_spot["latitude"], - longitude=source_spot["longitude"]) + dx_grid=source_spot["grid6"], + dx_latitude=source_spot["latitude"], + dx_longitude=source_spot["longitude"]) # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do # that for us. diff --git a/spotproviders/sota.py b/spotproviders/sota.py index 5380dcf..104b75d 100644 --- a/spotproviders/sota.py +++ b/spotproviders/sota.py @@ -58,9 +58,9 @@ class SOTA(HTTPSpotProvider): try: summit_response = self.SUMMIT_DATA_CACHE.get(self.SUMMIT_URL_ROOT + source_spot["summitCode"], headers=HTTP_HEADERS) summit_data = summit_response.json() - spot.grid = summit_data["locator"] - spot.latitude = summit_data["latitude"] - spot.longitude = summit_data["longitude"] + spot.dx_grid = summit_data["locator"] + spot.dx_latitude = summit_data["latitude"] + spot.dx_longitude = summit_data["longitude"] except Exception: logging.warn("Looking up summit " + source_spot["summitCode"] + " from the SOTA API failed. No summit data was available.") diff --git a/spotproviders/wwbota.py b/spotproviders/wwbota.py index 3c26c0a..4906089 100644 --- a/spotproviders/wwbota.py +++ b/spotproviders/wwbota.py @@ -23,22 +23,22 @@ class WWBOTA(SSESpotProvider): ref_names.append(ref["name"]) spot = Spot(source=self.name, - dx_call=source_spot["call"].upper(), - de_call=source_spot["spotter"].upper(), - freq=float(source_spot["freq"]) * 1000000, - mode=source_spot["mode"].upper(), - comment=source_spot["comment"], - sig="WWBOTA", - sig_refs=refs, - sig_refs_names=ref_names, - icon="radiation", - time=datetime.fromisoformat(source_spot["time"]).timestamp(), - # WWBOTA spots can contain multiple references for bunkers being activated simultaneously. For - # now, we will just pick the first one to use as our grid, latitude and longitude. - grid=source_spot["references"][0]["locator"], - latitude=source_spot["references"][0]["lat"], - longitude=source_spot["references"][0]["long"], - qrt=source_spot["type"] == "QRT") + dx_call=source_spot["call"].upper(), + de_call=source_spot["spotter"].upper(), + freq=float(source_spot["freq"]) * 1000000, + mode=source_spot["mode"].upper(), + comment=source_spot["comment"], + sig="WWBOTA", + sig_refs=refs, + sig_refs_names=ref_names, + icon="radiation", + time=datetime.fromisoformat(source_spot["time"]).timestamp(), + # WWBOTA spots can contain multiple references for bunkers being activated simultaneously. For + # now, we will just pick the first one to use as our grid, latitude and longitude. + dx_grid=source_spot["references"][0]["locator"], + dx_latitude=source_spot["references"][0]["lat"], + dx_longitude=source_spot["references"][0]["long"], + qrt=source_spot["type"] == "QRT") # WWBOTA does support a special "Test" spot type, we need to avoid adding that. return spot if source_spot["type"] != "Test" else None diff --git a/spotproviders/wwff.py b/spotproviders/wwff.py index 32e01cf..4b089f7 100644 --- a/spotproviders/wwff.py +++ b/spotproviders/wwff.py @@ -31,8 +31,8 @@ class WWFF(HTTPSpotProvider): sig_refs_names=[source_spot["reference_name"]], icon="seedling", time=datetime.fromtimestamp(source_spot["spot_time"], tz=pytz.UTC).timestamp(), - latitude=source_spot["latitude"], - longitude=source_spot["longitude"]) + dx_latitude=source_spot["latitude"], + dx_longitude=source_spot["longitude"]) # Add to our list. Don't worry about de-duping, removing old spots etc. at this point; other code will do # that for us. diff --git a/webassets/apidocs/openapi.yml b/webassets/apidocs/openapi.yml index d086c68..b0c8e7a 100644 --- a/webassets/apidocs/openapi.yml +++ b/webassets/apidocs/openapi.yml @@ -461,10 +461,6 @@ components: type: string description: Callsign of the operator that has been spotted example: M0TRT - de_call: - type: string - description: Callsign of the operator that has spotted them - example: M0TEST dx_name: type: string description: Name of the operator that has been spotted @@ -473,18 +469,10 @@ components: type: string description: Country of the DX operator example: United Kingdom - de_country: - type: string - description: Country of the spotter - example: United Kingdom dx_flag: type: string description: Country flag of the DX operator example: "" - de_flag: - type: string - description: Country flag of the spotter - example: "" dx_continent: type: string description: Continent of the DX operator @@ -497,26 +485,10 @@ components: - OC - AN example: EU - de_continent: - type: string - enum: - - EU - - NA - - SA - - AS - - AF - - OC - - AN - description: Continent of the spotter - example: EU dx_dxcc_id: type: integer description: DXCC ID of the DX operator example: 235 - de_dxcc_id: - type: integer - description: DXCC ID of the spotter - example: 235 dx_cq_zone: type: integer description: CQ zone of the DX operator @@ -529,6 +501,71 @@ components: type: string description: If this is an APRS spot, what SSID was the DX operator using? example: "" + dx_grid: + type: string + description: Maidenhead grid locator for the DX spot. This could be from a geographical reference e.g. POTA, or just from the country + example: IO91aa + dx_latitude: + type: number + description: Latitude of the DX spot, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup + example: 51.2345 + dx_longitude: + type: number + description: Longitude of the DX spot, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup + example: -1.2345 + dx_location_source: + type: string + description: Where we got the DX 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 + dx_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 + de_call: + type: string + description: Callsign of the operator that has spotted them + example: M0TEST + de_country: + type: string + description: Country of the spotter + example: United Kingdom + de_flag: + type: string + description: Country flag of the spotter + example: "" + de_continent: + type: string + enum: + - EU + - NA + - SA + - AS + - AF + - OC + - AN + description: Continent of the spotter + example: EU + de_dxcc_id: + type: integer + description: DXCC ID of the spotter + example: 235 + de_grid: + type: string + description: 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. + example: IO91aa + de_latitude: + type: number + description: Latitude of the spotter, 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. + example: 51.2345 + de_longitude: + type: number + description: Longitude of the DX spotspotter, 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. + example: -1.2345 mode: type: string description: Reported mode. @@ -651,31 +688,6 @@ components: type: string descripton: 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. example: tree - grid: - type: string - description: Maidenhead grid locator for the spot. This could be from a geographical reference e.g. POTA, or just from the country - example: IO91aa - latitude: - type: number - description: Latitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup - example: 51.2345 - longitude: - type: number - description: Latitude, in degrees. This could be from a geographical reference e.g. POTA, or from a QRZ lookup - 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: type: boolean description: QRT state. Some APIs return spots marked as QRT. Otherwise we can check the comments. diff --git a/webassets/js/spots.js b/webassets/js/spots.js index 8ed78d8..8ffa820 100644 --- a/webassets/js/spots.js +++ b/webassets/js/spots.js @@ -134,11 +134,11 @@ function updateTable() { // Format bearing text var bearingText = "---"; - if (userPos != null && s["latitude"] != null && s["longitude"] != null) { - var bearing = calcBearing(userPos[0], userPos[1], s["latitude"], s["longitude"]); + if (userPos != null && s["dx_latitude"] != null && s["dx_longitude"] != null) { + var bearing = calcBearing(userPos[0], userPos[1], s["dx_latitude"], s["dx_longitude"]); bearingText = bearing.toFixed(0).padStart(3, '0') + "°"; - if (s["location_good"] == null || s["location_good"] == false) { - if (s["location_source"] == "QRZ") { + if (s["dx_location_good"] == null || s["dx_location_good"] == false) { + if (s["dx_location_source"] == "QRZ") { bearingText = bearingText + ""; } else { bearingText = bearingText + "";