mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-08-06 10:31:42 +00:00
134 lines
6.7 KiB
Python
134 lines
6.7 KiB
Python
import logging
|
|
|
|
from pyhamtools.locator import latlong_to_locator, locator_to_latlong
|
|
from requests.exceptions import ConnectionError, ReadTimeout, ConnectTimeout
|
|
|
|
from core.constants import SIGS, HTTP_HEADERS
|
|
from core.data_store import DATA_STORE
|
|
from core.geo_utils import wab_wai_square_to_lat_lon
|
|
from core.url_data_cache import URL_DATA_CACHE
|
|
|
|
|
|
def get_ref_regex_for_sig(sig):
|
|
"""Utility function to get the regex string for a SIG reference for a named SIG. If no match is found, None will be returned."""
|
|
|
|
for s in SIGS:
|
|
if s.name.upper() == sig.upper():
|
|
return s.ref_regex
|
|
return None
|
|
|
|
|
|
def get_sig_name_from_comment_name(sig):
|
|
"""Utility function to get the name of a SIG from its "comment name". Generally these will be the same but there are
|
|
some cases (e.g. is "TOTA" Towers, Tiles or Toilets?) where we need to transform one to the other."""
|
|
|
|
for s in SIGS:
|
|
if any(n.upper() == sig.upper() for n in s.comment_names):
|
|
return s.name
|
|
return None
|
|
|
|
|
|
def populate_sig_ref_info(sig_ref):
|
|
"""Look up details of a SIG reference (e.g. POTA park) such as name, lat/lon, and grid. Takes in a sig_ref object
|
|
which must at minimum have a "sig" and an "id". The rest of the object will be populated and returned. This makes
|
|
use of SIG ref data in the data store, live lookups from the web, or just automatic calculation depending on which
|
|
SIG we are getting data for.
|
|
Note there is currently no support for KRMNPA location lookup, see issue #61."""
|
|
|
|
if sig_ref.sig is None or sig_ref.sig == "" or sig_ref.id is None or sig_ref.id == "":
|
|
logging.debug("Failed to look up sig_ref info, sig or id were not set.")
|
|
return sig_ref
|
|
|
|
sig = sig_ref.sig
|
|
ref_id = sig_ref.id
|
|
|
|
# DME fudge. Our database has leading zeros padding to 5 digits which is the expected format, but not all activators
|
|
# add leading zeros.
|
|
if sig.upper() == "DME":
|
|
ref_id = ref_id.zfill(5)
|
|
|
|
try:
|
|
# If the SIG is HEMA or KRMNPA, we have no current lookup for this so just skip it.
|
|
if sig.upper() == "HEMA" or sig.upper() == "KRMNPA":
|
|
return sig_ref
|
|
|
|
# If the SIG is Tiles, WAB, WAI or BOTA (Beaches), we don't have anything to look up from the data store, we can
|
|
# calculate all the information we are going to get directly. So handle those cases first
|
|
elif sig.upper() == "TILES":
|
|
# Tiles on the Air just uses Maidenhead 6-digit squares, so ID, Name and Grid are all the same
|
|
if not sig_ref.name:
|
|
sig_ref.name = sig_ref.id
|
|
if not sig_ref.grid:
|
|
sig_ref.grid = sig_ref.id
|
|
if sig_ref.grid and not sig_ref.latitude:
|
|
ll = locator_to_latlong(str(sig_ref.grid))
|
|
sig_ref.latitude = ll[0]
|
|
sig_ref.longitude = ll[1]
|
|
|
|
elif sig.upper() == "WAB" or sig.upper() == "WAI":
|
|
ll = wab_wai_square_to_lat_lon(ref_id)
|
|
if ll:
|
|
sig_ref.name = ref_id
|
|
try:
|
|
sig_ref.grid = latlong_to_locator(ll[0], ll[1], 6)
|
|
sig_ref.latitude = ll[0]
|
|
sig_ref.longitude = ll[1]
|
|
except:
|
|
logging.warning("Invalid lat/lon received for WAB/WAI reference")
|
|
|
|
elif sig.upper() == "BOTA":
|
|
# For BOTA all we can ever generate is the URL, there is no data file or lookup for lat/longs
|
|
if not sig_ref.name:
|
|
sig_ref.name = sig_ref.id
|
|
sig_ref.url = "https://www.beachesontheair.com/beaches/" + sig_ref.name.lower().replace(" ", "-")
|
|
|
|
# OK, this is something we have to look up. Now check to see if our data store contains SIG ref information for
|
|
# this SIG. If so, check for the reference data and use that.
|
|
elif sig in DATA_STORE.sigrefs:
|
|
lookup_data = DATA_STORE.sigrefs[sig][ref_id] if ref_id in DATA_STORE.sigrefs[sig] else None
|
|
if lookup_data:
|
|
# Copy new sig ref data into existing object
|
|
sig_ref.__dict__.update(lookup_data.__dict__)
|
|
else:
|
|
logging.warning("%s database did not contain data for ref %s", sig, ref_id)
|
|
|
|
elif False:
|
|
# TODO remove
|
|
# OK, this is not a SIG we have stored data for. Maybe it's of a type we can query information for live.
|
|
if sig.upper() == "IOTA":
|
|
response = URL_DATA_CACHE.get("https://www.cqgma.org/api/ref/?" + ref_id,
|
|
headers=HTTP_HEADERS)
|
|
if response.ok:
|
|
data = response.json()
|
|
if data:
|
|
sig_ref.name = data["name"] if "name" in data else None
|
|
sig_ref.url = "https://www.cqgma.org/zinfo.php?ref=" + ref_id
|
|
sig_ref.grid = data["locator"] if "locator" in data else None
|
|
|
|
# For some things (just IOTA?) the GMA actually returns a box where "latitude" and "longitude" are
|
|
# the zeroest corner of the box, then "lat2" and "lng2" provide the other corner. We detect this
|
|
# and provide a single lat/lon for the centre. Otherwise if we don't have these extra parameters,
|
|
# just use the single point we have.
|
|
if data.get("latitude") is not None and data.get("longitude") is not None and data.get(
|
|
"lat2") is not None and data.get("lng2") is not None:
|
|
sig_ref.latitude = (float(data["latitude"]) + float(data["lat2"])) / 2.0
|
|
sig_ref.longitude = (float(data["longitude"]) + float(data["lng2"])) / 2.0
|
|
else:
|
|
sig_ref.latitude = float(data["latitude"]) if data.get("latitude") is not None else None
|
|
sig_ref.longitude = float(data["longitude"]) if data.get("longitude") is not None else None
|
|
elif not response.from_cache:
|
|
logging.warning("Malformed response looking up %s ref %s via GMA", sig, ref_id)
|
|
elif not response.from_cache:
|
|
logging.warning("HTTP %d looking up %s ref %s", response.status_code, sig, ref_id)
|
|
|
|
else:
|
|
logging.warning(f"Tried to look up a SIG called %s but Spothole does not know what that is.", sig)
|
|
|
|
except Exception:
|
|
logging.error("Exception when looking up sig_ref info for " + sig + " ref " + ref_id, exc_info=True)
|
|
return sig_ref
|
|
|
|
|
|
# Regex matching any SIG's "comment name", i.e. how it may be referred to in spot comments
|
|
ANY_SIG_REGEX = r"(" + r"|".join(n for s in SIGS for n in s.comment_names) + r")"
|