3 Commits
Author SHA1 Message Date
Ian Renton e4c3a52299 Add support for DME. Closes #114. 2026-06-23 06:45:03 +01:00
Ian Renton 21a3ae70b5 Add support for DME. Closes #114. 2026-06-23 06:38:41 +01:00
Ian Renton d4d43a43c8 DME geodata CSV #114 2026-06-21 22:39:36 +01:00
16 changed files with 8171 additions and 19 deletions
+2
View File
@@ -19,6 +19,8 @@ Supported data sources include DX Clusters, the Reverse Beacon Network (RBN), th
SOTA, WWFF, GMA, WWBOTA, HEMA, Parks 'n' Peaks, ZLOTA, WOTA, BOTA, LLOTA, WWTOTA, Tiles on the Air, the UK Packet SOTA, WWFF, GMA, WWBOTA, HEMA, Parks 'n' Peaks, ZLOTA, WOTA, BOTA, LLOTA, WWTOTA, Tiles on the Air, the UK Packet
Repeater Network, NG3K, and any site based on the xOTA software by nischu. Repeater Network, NG3K, and any site based on the xOTA software by nischu.
Additional Special Interest Groups (SIGs) without their own specific data source include WAB, WAI and DME.
![Screenshot](/images/screenshot2.png) ![Screenshot](/images/screenshot2.png)
![Screenshot](/images/screenshot3.png) ![Screenshot](/images/screenshot3.png)
+1
View File
@@ -32,6 +32,7 @@ SIGS = [
SIG(name="Tiles", description="Tiles on the Air", ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}"), SIG(name="Tiles", description="Tiles on the Air", ref_regex=r"[A-Za-z]{2}[0-9]{2}[A-Za-z]{2}"),
SIG(name="WAB", description="Worked All Britain", ref_regex=r"[A-Z]{1,2}[0-9]{2}"), SIG(name="WAB", description="Worked All Britain", ref_regex=r"[A-Z]{1,2}[0-9]{2}"),
SIG(name="WAI", description="Worked All Ireland", ref_regex=r"[A-Z][0-9]{2}"), SIG(name="WAI", description="Worked All Ireland", ref_regex=r"[A-Z][0-9]{2}"),
SIG(name="DME", description="Diplomas de Municipios Españoles", ref_regex=r"\d{4,5}"),
SIG(name="TOTA", description="Toilets on the Air", ref_regex=r"T\-[0-9]{2}") SIG(name="TOTA", description="Toilets on the Air", ref_regex=r"T\-[0-9]{2}")
] ]
+14
View File
@@ -7,6 +7,9 @@ from core.cache_utils import SEMI_STATIC_URL_DATA_CACHE
from core.constants import SIGS, HTTP_HEADERS from core.constants import SIGS, HTTP_HEADERS
from core.geo_utils import wab_wai_square_to_lat_lon from core.geo_utils import wab_wai_square_to_lat_lon
with open("datafiles/dme-geodata.csv", encoding="utf-8") as _f:
_DME_INDEX = {row["dme"]: row for row in csv.DictReader(_f)}
def get_ref_regex_for_sig(sig): 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.""" """Utility function to get the regex string for a SIG reference for a named SIG. If no match is found, None will be returned."""
@@ -188,6 +191,17 @@ def populate_sig_ref_info(sig_ref):
sig_ref.longitude = ll[1] sig_ref.longitude = ll[1]
except: except:
logging.debug("Invalid lat/lon received for reference") logging.debug("Invalid lat/lon received for reference")
elif sig.upper() == "DME":
row = _DME_INDEX.get(ref_id)
if row:
sig_ref.name = row["municipio"] + ", " + row["provincia"]
sig_ref.latitude = float(row["lat"]) if row.get("lat") else None
sig_ref.longitude = float(row["lon"]) if row.get("lon") else None
if sig_ref.latitude and sig_ref.longitude:
try:
sig_ref.grid = latlong_to_locator(sig_ref.latitude, sig_ref.longitude, 6)
except Exception:
logging.debug("Invalid lat/lon received for reference")
except Exception: except Exception:
logging.warning("Failed to look up sig_ref info for " + sig + " ref " + ref_id, exc_info=True) logging.warning("Failed to look up sig_ref info for " + sig + " ref " + ref_id, exc_info=True)
return sig_ref return sig_ref
+1 -1
View File
@@ -263,7 +263,7 @@ class Spot:
# If so, add that to the sig_refs list for this spot. # If so, add that to the sig_refs list for this spot.
ref_regex = get_ref_regex_for_sig(found_sig) ref_regex = get_ref_regex_for_sig(found_sig)
if ref_regex: if ref_regex:
ref_matches = re.finditer(r"(^|\W)" + found_sig + r"($|\W)(" + ref_regex + r")($|\W)", self.comment, ref_matches = re.finditer(r"(^|\W)" + found_sig + r"([ -])(" + ref_regex + r")($|\W)", self.comment,
re.IGNORECASE) re.IGNORECASE)
for ref_match in ref_matches: for ref_match in ref_matches:
self._append_sig_ref_if_missing(SIGRef(id=ref_match.group(3).upper(), sig=found_sig)) self._append_sig_ref_if_missing(SIGRef(id=ref_match.group(3).upper(), sig=found_sig))
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -101,9 +101,9 @@
(MOTA), the Amateur Radio Lighthouse Socirty (ARLHS), International Lighthouse Lightship Weekend (ILLW), Silos (MOTA), the Amateur Radio Lighthouse Socirty (ARLHS), International Lighthouse Lightship Weekend (ILLW), Silos
on the Air (SIOTA), World Castles Award (WCA), New Zealand on the Air (ZLOTA), Keith Roget Memorial National on the Air (SIOTA), World Castles Award (WCA), New Zealand on the Air (ZLOTA), Keith Roget Memorial National
Parks Award (KRMNPA), Wainwrights on the Air (WOTA), Beaches on the Air (BOTA), Lagos y Lagunas On the Air Parks Award (KRMNPA), Wainwrights on the Air (WOTA), Beaches on the Air (BOTA), Lagos y Lagunas On the Air
(LLOTA), Towers on the Air (WWTOTA), Tiles on the Air, Worked All Britain (WAB), Worked All Ireland (WAI), and (LLOTA), Towers on the Air (WWTOTA), Tiles on the Air, Worked All Britain (WAB), Worked All Ireland (WAI), el
Toilets on the Air (TOTA).</p> Diploma Municipios de España (DME) and Toilets on the Air (TOTA).</p>
<p>As of the time of writing in November 2025, I think Spothole captures essentially all outdoor radio programmes <p>As of the time of writing in June 2026, I think Spothole captures essentially all outdoor radio programmes
that have a defined reference list, and almost certainly those that have a spotting/alerting API. If you know of that have a defined reference list, and almost certainly those that have a spotting/alerting API. If you know of
one I've missed, please let me know!</p> one I've missed, please let me know!</p>
<h4 class="mt-4">Why can I filter spots by both SIG and Source? Isn't that basically the same thing?</h4> <h4 class="mt-4">Why can I filter spots by both SIG and Source? Isn't that basically the same thing?</h4>
+1 -1
View File
@@ -76,7 +76,7 @@
</div> </div>
<script src="/js/add-spot.js?v=1782076701"></script> <script src="/js/add-spot.js?v=1782193503"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active"); $("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -75,7 +75,7 @@
</div> </div>
<script src="/js/alerts.js?v=1782076701"></script> <script src="/js/alerts.js?v=1782193504"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active"); $("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -77,8 +77,8 @@
<script> <script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %}; let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script> </script>
<script src="/js/spotsbandsandmap.js?v=1782076701"></script> <script src="/js/spotsbandsandmap.js?v=1782193503"></script>
<script src="/js/bands.js?v=1782076701"></script> <script src="/js/bands.js?v=1782193503"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-bands").addClass("active"); $("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %} {% extends "skeleton.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/css/style.css?v=1782076701" type="text/css"> <link rel="stylesheet" href="/css/style.css?v=1782193503" type="text/css">
<link href="/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet"> <link href="/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet"> <link href="/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/vendor/css/solid-6.7.2.min.css" rel="stylesheet"> <link href="/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -10,10 +10,10 @@
<script src="/vendor/js/bootstrap-5.3.8.bundle.min.js"></script> <script src="/vendor/js/bootstrap-5.3.8.bundle.min.js"></script>
<script src="/vendor/js/tinycolor2-1.6.0.min.js"></script> <script src="/vendor/js/tinycolor2-1.6.0.min.js"></script>
<script src="/js/utils.js?v=1782076701"></script> <script src="/js/utils.js?v=1782193503"></script>
<script src="/js/ui-ham.js?v=1782076701"></script> <script src="/js/ui-ham.js?v=1782193503"></script>
<script src="/js/geo.js?v=1782076701"></script> <script src="/js/geo.js?v=1782193503"></script>
<script src="/js/common.js?v=1782076701"></script> <script src="/js/common.js?v=1782193503"></script>
{% end %} {% end %}
{% block body %} {% block body %}
<div class="container"> <div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div> </div>
<script src="/vendor/js/chart-4.4.9.umd.min.js"></script> <script src="/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/js/conditions.js?v=1782076701"></script> <script src="/js/conditions.js?v=1782193503"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active"); $("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -95,8 +95,8 @@
<script> <script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %}; let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script> </script>
<script src="/js/spotsbandsandmap.js?v=1782076701"></script> <script src="/js/spotsbandsandmap.js?v=1782193504"></script>
<script src="/js/map.js?v=1782076701"></script> <script src="/js/map.js?v=1782193504"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-map").addClass("active"); $("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -116,8 +116,8 @@
<script> <script>
let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %}; let spotProvidersEnabledByDefault = {% raw json_encode(web_ui_options["spot-providers-enabled-by-default"]) %};
</script> </script>
<script src="/js/spotsbandsandmap.js?v=1782076701"></script> <script src="/js/spotsbandsandmap.js?v=1782193503"></script>
<script src="/js/spots.js?v=1782076701"></script> <script src="/js/spots.js?v=1782193503"></script>
<script>$(document).ready(function () { <script>$(document).ready(function () {
$("#nav-link-spots").addClass("active"); $("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script> }); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -59,7 +59,7 @@
</div> </div>
</div> </div>
<script src="/js/status.js?v=1782076701"></script> <script src="/js/status.js?v=1782193503"></script>
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$("#nav-link-status").addClass("active"); $("#nav-link-status").addClass("active");
+1
View File
@@ -803,6 +803,7 @@ components:
- Tiles - Tiles
- WAB - WAB
- WAI - WAI
- DME
- TOTA - TOTA
example: POTA example: POTA
+1
View File
@@ -335,6 +335,7 @@ const SIG_ICONS = {
"WWTOTA": "fa-tower-observation", "WWTOTA": "fa-tower-observation",
"WAB": "fa-table-cells-large", "WAB": "fa-table-cells-large",
"WAI": "fa-table-cells-large", "WAI": "fa-table-cells-large",
"DME": "fa-building",
"Tiles": "fa-square", "Tiles": "fa-square",
"TOTA": "fa-toilet" "TOTA": "fa-toilet"
} }