mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 16:59:25 +00:00
Allow table columns to be toggled on and off. #19
This commit is contained in:
@@ -113,6 +113,7 @@ def infer_country_from_callsign(call):
|
|||||||
|
|
||||||
# Infer a DXCC ID from a callsign
|
# Infer a DXCC ID from a callsign
|
||||||
def infer_dxcc_id_from_callsign(call):
|
def infer_dxcc_id_from_callsign(call):
|
||||||
|
get_clublog_xml_data_for_callsign("M0TRT")
|
||||||
try:
|
try:
|
||||||
# Start with the basic country-files.com-based decoder.
|
# Start with the basic country-files.com-based decoder.
|
||||||
dxcc = CALL_INFO_BASIC.get_adif_id(call)
|
dxcc = CALL_INFO_BASIC.get_adif_id(call)
|
||||||
@@ -225,7 +226,8 @@ def get_qrz_data_for_callsign(call):
|
|||||||
QRZ_CALLSIGN_DATA_CACHE.add(call, data, expire=604800) # 1 week in seconds
|
QRZ_CALLSIGN_DATA_CACHE.add(call, data, expire=604800) # 1 week in seconds
|
||||||
return data
|
return data
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# QRZ had no info for the call, that's OK
|
# QRZ had no info for the call, that's OK. Cache a None so we don't try to look this up again
|
||||||
|
QRZ_CALLSIGN_DATA_CACHE.add(call, None, expire=604800) # 1 week in seconds
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@@ -243,7 +245,8 @@ def get_clublog_api_data_for_callsign(call):
|
|||||||
CLUBLOG_CALLSIGN_DATA_CACHE.add(call, data, expire=604800) # 1 week in seconds
|
CLUBLOG_CALLSIGN_DATA_CACHE.add(call, data, expire=604800) # 1 week in seconds
|
||||||
return data
|
return data
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Clublog had no info for the call, that's OK
|
# Clublog had no info for the call, that's OK. Cache a None so we don't try to look this up again
|
||||||
|
CLUBLOG_CALLSIGN_DATA_CACHE.add(call, None, expire=604800) # 1 week in seconds
|
||||||
return None
|
return None
|
||||||
except APIKeyMissingError:
|
except APIKeyMissingError:
|
||||||
# User API key was wrong, warn
|
# User API key was wrong, warn
|
||||||
@@ -260,7 +263,8 @@ def get_clublog_xml_data_for_callsign(call):
|
|||||||
data = LOOKUP_LIB_CLUBLOG_XML.lookup_callsign(callsign=call)
|
data = LOOKUP_LIB_CLUBLOG_XML.lookup_callsign(callsign=call)
|
||||||
return data
|
return data
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Clublog had no info for the call, that's OK
|
# Clublog had no info for the call, that's OK. Cache a None so we don't try to look this up again
|
||||||
|
CLUBLOG_CALLSIGN_DATA_CACHE.add(call, None, expire=604800) # 1 week in seconds
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
@@ -310,11 +314,21 @@ def infer_latlon_from_callsign_dxcc(call):
|
|||||||
try:
|
try:
|
||||||
data = CALL_INFO_BASIC.get_lat_long(call)
|
data = CALL_INFO_BASIC.get_lat_long(call)
|
||||||
if data and "latitude" in data and "longitude" in data:
|
if data and "latitude" in data and "longitude" in data:
|
||||||
return [data["latitude"], data["longitude"]]
|
loc = [data["latitude"], data["longitude"]]
|
||||||
else:
|
else:
|
||||||
return None
|
loc = None
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
loc = None
|
||||||
|
# Couldn't get anything from basic call info database, try Clublog data
|
||||||
|
if not loc:
|
||||||
|
data = get_clublog_xml_data_for_callsign(call)
|
||||||
|
if data and "Lat" in data and "Lon" in data:
|
||||||
|
loc = [data["Lat"], data["Lon"]]
|
||||||
|
if not loc:
|
||||||
|
data = get_clublog_api_data_for_callsign(call)
|
||||||
|
if data and "Lat" in data and "Lon" in data:
|
||||||
|
loc = [data["Lat"], data["Lon"]]
|
||||||
|
return loc
|
||||||
|
|
||||||
|
|
||||||
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
|
# Infer a grid locator from a callsign (using DXCC, probably very inaccurate)
|
||||||
|
|||||||
@@ -80,31 +80,31 @@
|
|||||||
<h5 class="card-title">Table Data</h5>
|
<h5 class="card-title">Table Data</h5>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowStartTime" value="tableShowStartTime" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowStartTime" value="tableShowStartTime" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowStartTime">Start Time</label>
|
<label class="form-check-label" for="tableShowStartTime">Start Time</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowEndTime" value="tableShowEndTime" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowEndTime" value="tableShowEndTime" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowEndTime">End Time</label>
|
<label class="form-check-label" for="tableShowEndTime">End Time</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDX" value="tableShowDX" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDX" value="tableShowDX" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowDX">DX</label>
|
<label class="form-check-label" for="tableShowDX">DX</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowFreqsModes" value="tableShowFreqsModes" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowFreqsModes" value="tableShowFreqsModes" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowFreqsModes">Frequencies & Modes</label>
|
<label class="form-check-label" for="tableShowFreqsModes">Frequencies & Modes</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowComment" value="tableShowComment" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowComment" value="tableShowComment" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowComment">Comment</label>
|
<label class="form-check-label" for="tableShowComment">Comment</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowSource" value="tableShowSource" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowSource" value="tableShowSource" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowSource">Source</label>
|
<label class="form-check-label" for="tableShowSource">Source</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowRef" value="tableShowRef" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowRef" value="tableShowRef" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowRef">Ref.</label>
|
<label class="form-check-label" for="tableShowRef">Ref.</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -92,42 +92,42 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Table Data</h5>
|
<h5 class="card-title">Table Columns</h5>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowTime" value="tableShowTime" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowTime" value="tableShowTime" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowTime">Time</label>
|
<label class="form-check-label" for="tableShowTime">Time</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDX" value="tableShowDX" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDX" value="tableShowDX" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowDX">DX</label>
|
<label class="form-check-label" for="tableShowDX">DX</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowFreq" value="tableShowFreq" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowFreq" value="tableShowFreq" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowFreq">Frequency</label>
|
<label class="form-check-label" for="tableShowFreq">Frequency</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowMode" value="tableShowMode" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowMode" value="tableShowMode" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowMode">Mode</label>
|
<label class="form-check-label" for="tableShowMode">Mode</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowComment" value="tableShowComment" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowComment" value="tableShowComment" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowComment">Comment</label>
|
<label class="form-check-label" for="tableShowComment">Comment</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowBearing" value="tableShowBearing" disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowBearing" value="tableShowBearing" oninput="columnsUpdated();">
|
||||||
<label class="form-check-label" for="tableShowBearing">Bearing</label>
|
<label class="form-check-label" for="tableShowBearing">Bearing</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowSource" value="tableShowSource" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowSource" value="tableShowSource" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowSource">Source</label>
|
<label class="form-check-label" for="tableShowSource">Source</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowRef" value="tableShowRef" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowRef" value="tableShowRef" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowRef">Ref.</label>
|
<label class="form-check-label" for="tableShowRef">Ref.</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDE" value="tableShowDE" checked disabled>
|
<input class="form-check-input storeable-checkbox" type="checkbox" id="tableShowDE" value="tableShowDE" oninput="columnsUpdated();" checked>
|
||||||
<label class="form-check-label" for="tableShowDE">DE</label>
|
<label class="form-check-label" for="tableShowDE">DE</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
<h5 class="card-title">Location</h5>
|
<h5 class="card-title">Location</h5>
|
||||||
<div class="form-group spothole-card-text">
|
<div class="form-group spothole-card-text">
|
||||||
<label for="userGrid">Your grid:</label>
|
<label for="userGrid">Your grid:</label>
|
||||||
<input type="text" class="storeable-text form-control" id="userGrid" placeholder="AA00aa" style="width: 10em; display: inline-block;">
|
<input type="text" class="storeable-text form-control" id="userGrid" placeholder="AA00aa" oninput="userGridUpdated();" style="width: 10em; display: inline-block;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,15 +41,38 @@ function updateTable() {
|
|||||||
// Use local time instead of UTC?
|
// Use local time instead of UTC?
|
||||||
var useLocalTime = $("#timeZone")[0].value == "local";
|
var useLocalTime = $("#timeZone")[0].value == "local";
|
||||||
|
|
||||||
|
// Table data toggles
|
||||||
|
var showStartTime = $("#tableShowStartTime")[0].checked;
|
||||||
|
var showEndTime = $("#tableShowEndTime")[0].checked;
|
||||||
|
var showDX = $("#tableShowDX")[0].checked;
|
||||||
|
var showFreqsModes = $("#tableShowFreqsModes")[0].checked;
|
||||||
|
var showComment = $("#tableShowComment")[0].checked;
|
||||||
|
var showSource = $("#tableShowSource")[0].checked;
|
||||||
|
var showRef = $("#tableShowRef")[0].checked;
|
||||||
|
|
||||||
// Populate table with headers
|
// Populate table with headers
|
||||||
let table = $('<table class="table table-striped-custom table-hover">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
let table = $('<table class="table table-striped-custom table-hover">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
||||||
table.find('thead tr').append(`<th>${useLocalTime ? "Start (Local)" : "Start UTC"}</th>`);
|
if (showStartTime) {
|
||||||
table.find('thead tr').append(`<th>${useLocalTime ? "End (Local)" : "End UTC"}</th>`);
|
table.find('thead tr').append(`<th>${useLocalTime ? "Start (Local)" : "Start UTC"}</th>`);
|
||||||
table.find('thead tr').append(`<th>DX</th>`);
|
}
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Freq<span class='hideonmobile'>uencie</span>s & Modes</th>`);
|
if (showEndTime) {
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Comment</th>`);
|
table.find('thead tr').append(`<th>${useLocalTime ? "End (Local)" : "End UTC"}</th>`);
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Source</th>`);
|
}
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Ref.</th>`);
|
if (showDX) {
|
||||||
|
table.find('thead tr').append(`<th>DX</th>`);
|
||||||
|
}
|
||||||
|
if (showFreqsModes) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Freq<span class='hideonmobile'>uencie</span>s & Modes</th>`);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Comment</th>`);
|
||||||
|
}
|
||||||
|
if (showSource) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Source</th>`);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Ref.</th>`);
|
||||||
|
}
|
||||||
|
|
||||||
// Split alerts into three types, each of which will get its own table header: On now, next 24h, and later. "On now"
|
// Split alerts into three types, each of which will get its own table header: On now, next 24h, and later. "On now"
|
||||||
// is considered to be events with an end_time where start<now<end, or events with no end time that started in the
|
// is considered to be events with an end_time where start<now<end, or events with no end time that started in the
|
||||||
@@ -91,6 +114,15 @@ function addAlertRowsToTable(tbody, alerts) {
|
|||||||
// Use local time instead of UTC?
|
// Use local time instead of UTC?
|
||||||
var useLocalTime = $("#timeZone")[0].value == "local";
|
var useLocalTime = $("#timeZone")[0].value == "local";
|
||||||
|
|
||||||
|
// Table data toggles
|
||||||
|
var showStartTime = $("#tableShowStartTime")[0].checked;
|
||||||
|
var showEndTime = $("#tableShowEndTime")[0].checked;
|
||||||
|
var showDX = $("#tableShowDX")[0].checked;
|
||||||
|
var showFreqsModes = $("#tableShowFreqsModes")[0].checked;
|
||||||
|
var showComment = $("#tableShowComment")[0].checked;
|
||||||
|
var showSource = $("#tableShowSource")[0].checked;
|
||||||
|
var showRef = $("#tableShowRef")[0].checked;
|
||||||
|
|
||||||
// Get times for the alert, and convert to local time if necessary.
|
// Get times for the alert, and convert to local time if necessary.
|
||||||
var start_time_unix = moment.unix(a["start_time"]);
|
var start_time_unix = moment.unix(a["start_time"]);
|
||||||
var start_time = start_time_unix.utc();
|
var start_time = start_time_unix.utc();
|
||||||
@@ -181,18 +213,45 @@ function addAlertRowsToTable(tbody, alerts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate the row
|
// Populate the row
|
||||||
$tr.append(`<td class='nowrap'>${start_time_formatted}</td>`);
|
if (showStartTime) {
|
||||||
$tr.append(`<td class='nowrap'>${end_time_formatted}</td>`);
|
$tr.append(`<td class='nowrap'>${start_time_formatted}</td>`);
|
||||||
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span>${dx_calls_html}${dx_country_html}</td>`);
|
}
|
||||||
$tr.append(`<td class='hideonmobile'>${freqsModesText}</td>`);
|
if (showEndTime) {
|
||||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
$tr.append(`<td class='nowrap'>${end_time_formatted}</td>`);
|
||||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> ${sigSourceText}</td>`);
|
}
|
||||||
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
if (showDX) {
|
||||||
|
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span>${dx_calls_html}${dx_country_html}</td>`);
|
||||||
|
}
|
||||||
|
if (showFreqsModes) {
|
||||||
|
$tr.append(`<td class='hideonmobile'>${freqsModesText}</td>`);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||||
|
}
|
||||||
|
if (showSource) {
|
||||||
|
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> ${sigSourceText}</td>`);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
$tr.append(`<td class='hideonmobile'>${sig_refs}</td>`);
|
||||||
|
}
|
||||||
tbody.append($tr);
|
tbody.append($tr);
|
||||||
|
|
||||||
// Second row for mobile view only, containing source, ref, freqs/modes & comment
|
// Second row for mobile view only, containing source, ref, freqs/modes & comment
|
||||||
$tr2 = $("<tr class='hidenotonmobile'>");
|
$tr2 = $("<tr class='hidenotonmobile'>");
|
||||||
$tr2.append(`<td colspan="100"><span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> ${sig_refs} ${freqsModesText}<br/>${commentText}</td>`);
|
$td2 = $("<td colspan='100'>");
|
||||||
|
if (showSource) {
|
||||||
|
$td2.append(`<span class='icon-wrapper'><i class='fa-solid fa-${a["icon"]}'></i></span> `);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
$td2.append(`${sig_refs} `);
|
||||||
|
}
|
||||||
|
if (showFreqsModes) {
|
||||||
|
$td2.append(`${freqsModesText} `);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
$td2.append(`<br/>${commentText} `);
|
||||||
|
}
|
||||||
|
$tr2.append($td2);
|
||||||
tbody.append($tr2);
|
tbody.append($tr2);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,97 @@ function timeZoneUpdated() {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When one of the column toggle checkboxes are changed, reload the table and save settings
|
||||||
|
function columnsUpdated() {
|
||||||
|
updateTable();
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a Maidenhead grid reference of arbitrary precision to the lat/long of the centre point of the square.
|
||||||
|
// Returns null if the grid format is invalid.
|
||||||
|
function latLonForGridCentre(grid) {
|
||||||
|
let [lat, lon, latCellSize, lonCellSize] = latLonForGridSWCornerPlusSize(grid);
|
||||||
|
if (lat != null && lon != null && latCellSize != null && lonCellSize != null) {
|
||||||
|
return [lat + latCellSize / 2.0, lon + lonCellSize / 2.0];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a Maidenhead grid reference of arbitrary precision to lat/long, including in the result the size of the
|
||||||
|
// lowest grid square. This is a utility method used by the main methods that return the centre, southwest, and
|
||||||
|
// northeast coordinates of a grid square.
|
||||||
|
// The return type is always an array of size 4. The elements in it are null if the grid format is invalid.
|
||||||
|
function latLonForGridSWCornerPlusSize(grid) {
|
||||||
|
// Make sure we are in upper case so our maths works. Case is arbitrary for Maidenhead references
|
||||||
|
grid = grid.toUpperCase();
|
||||||
|
|
||||||
|
// Return null if our Maidenhead string is invalid or too short
|
||||||
|
let len = grid.length;
|
||||||
|
if (len <= 0 || (len % 2) !== 0) {
|
||||||
|
return [null, null, null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
let lat = 0.0; // aggregated latitude
|
||||||
|
let lon = 0.0; // aggregated longitude
|
||||||
|
let latCellSize = 10; // Size in degrees latitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||||
|
let lonCellSize = 20; // Size in degrees longitude of the current cell. Starts at 20 and gets smaller as the calculation progresses
|
||||||
|
let latCellNo; // grid latitude cell number this time
|
||||||
|
let lonCellNo; // grid longitude cell number this time
|
||||||
|
|
||||||
|
// Iterate through blocks (two-character sections)
|
||||||
|
for (let block = 0; block * 2 < len; block += 1) {
|
||||||
|
if (block % 2 === 0) {
|
||||||
|
// Letters in this block
|
||||||
|
lonCellNo = grid.charCodeAt(block * 2) - 'A'.charCodeAt(0);
|
||||||
|
latCellNo = grid.charCodeAt(block * 2 + 1) - 'A'.charCodeAt(0);
|
||||||
|
// Bail if the values aren't in range. Allowed values are A-R (0-17) for the first letter block, or
|
||||||
|
// A-X (0-23) thereafter.
|
||||||
|
let maxCellNo = (block === 0) ? 17 : 23;
|
||||||
|
if (latCellNo < 0 || latCellNo > maxCellNo || lonCellNo < 0 || lonCellNo > maxCellNo) {
|
||||||
|
return [null, null, null, null];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Numbers in this block
|
||||||
|
lonCellNo = parseInt(grid.charAt(block * 2));
|
||||||
|
latCellNo = parseInt(grid.charAt(block * 2 + 1));
|
||||||
|
// Bail if the values aren't in range 0-9..
|
||||||
|
if (latCellNo < 0 || latCellNo > 9 || lonCellNo < 0 || lonCellNo > 9) {
|
||||||
|
return [null, null, null, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregate the angles
|
||||||
|
lat += latCellNo * latCellSize;
|
||||||
|
lon += lonCellNo * lonCellSize;
|
||||||
|
|
||||||
|
// Reduce the cell size for the next block, unless we are on the last cell.
|
||||||
|
if (block * 2 < len - 2) {
|
||||||
|
// Still have more work to do, so reduce the cell size
|
||||||
|
if (block % 2 === 0) {
|
||||||
|
// Just dealt with letters, next block will be numbers so cells will be 1/10 the current size
|
||||||
|
latCellSize = latCellSize / 10.0;
|
||||||
|
lonCellSize = lonCellSize / 10.0;
|
||||||
|
} else {
|
||||||
|
// Just dealt with numbers, next block will be letters so cells will be 1/24 the current size
|
||||||
|
latCellSize = latCellSize / 24.0;
|
||||||
|
lonCellSize = lonCellSize / 24.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset back to (-180, -90) where the grid starts
|
||||||
|
lon -= 180.0;
|
||||||
|
lat -= 90.0;
|
||||||
|
|
||||||
|
// Return nulls on maths errors
|
||||||
|
if (isNaN(lat) || isNaN(lon) || isNaN(latCellSize) || isNaN(lonCellSize)) {
|
||||||
|
return [null, null, null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [lat, lon, latCellSize, lonCellSize];
|
||||||
|
}
|
||||||
|
|
||||||
// Save settings to local storage
|
// Save settings to local storage
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
// Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
|
// Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that
|
||||||
|
|||||||
@@ -34,16 +34,46 @@ function updateTable() {
|
|||||||
// Use local time instead of UTC?
|
// Use local time instead of UTC?
|
||||||
var useLocalTime = $("#timeZone")[0].value == "local";
|
var useLocalTime = $("#timeZone")[0].value == "local";
|
||||||
|
|
||||||
|
// Get user grid if valid, this will be null if it's not.
|
||||||
|
var userPos = latLonForGridCentre($("#userGrid").val());
|
||||||
|
|
||||||
|
// Table data toggles
|
||||||
|
var showTime = $("#tableShowTime")[0].checked;
|
||||||
|
var showDX = $("#tableShowDX")[0].checked;
|
||||||
|
var showFreq = $("#tableShowFreq")[0].checked;
|
||||||
|
var showMode = $("#tableShowMode")[0].checked;
|
||||||
|
var showComment = $("#tableShowComment")[0].checked;
|
||||||
|
var showBearing = $("#tableShowBearing")[0].checked && userPos != null;
|
||||||
|
var showSource = $("#tableShowSource")[0].checked;
|
||||||
|
var showRef = $("#tableShowRef")[0].checked;
|
||||||
|
var showDE = $("#tableShowDE")[0].checked;
|
||||||
|
|
||||||
// Populate table with headers
|
// Populate table with headers
|
||||||
let table = $('<table class="table table-striped-custom table-hover">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
let table = $('<table class="table table-striped-custom table-hover">').append('<thead><tr class="table-primary"></tr></thead><tbody></tbody>');
|
||||||
table.find('thead tr').append(`<th>${useLocalTime ? "Local" : "UTC"}</th>`);
|
if (showTime) {
|
||||||
table.find('thead tr').append(`<th>DX</th>`);
|
table.find('thead tr').append(`<th>${useLocalTime ? "Local" : "UTC"}</th>`);
|
||||||
table.find('thead tr').append(`<th>Freq<span class='hideonmobile'>uency</span></th>`);
|
}
|
||||||
table.find('thead tr').append(`<th>Mode</th>`);
|
if (showDX) {
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Comment</th>`);
|
table.find('thead tr').append(`<th>DX</th>`);
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Source</th>`);
|
}
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>Ref.</th>`);
|
if (showFreq) {
|
||||||
table.find('thead tr').append(`<th class='hideonmobile'>DE</th>`);
|
table.find('thead tr').append(`<th>Freq<span class='hideonmobile'>uency</span></th>`);
|
||||||
|
}
|
||||||
|
if (showMode) {
|
||||||
|
table.find('thead tr').append(`<th>Mode</th>`);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Comment</th>`);
|
||||||
|
}
|
||||||
|
if (showSource) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Source</th>`);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>Ref.</th>`);
|
||||||
|
}
|
||||||
|
if (showDE) {
|
||||||
|
table.find('thead tr').append(`<th class='hideonmobile'>DE</th>`);
|
||||||
|
}
|
||||||
|
|
||||||
if (spots.length == 0) {
|
if (spots.length == 0) {
|
||||||
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No spots match your filters.</td></tr>');
|
table.find('tbody').append('<tr class="table-danger"><td colspan="100" style="text-align:center;">No spots match your filters.</td></tr>');
|
||||||
@@ -142,14 +172,30 @@ function updateTable() {
|
|||||||
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
var bandFullName = s['band'] ? s['band'] + " band": "Unknown band";
|
||||||
|
|
||||||
// Populate the row
|
// Populate the row
|
||||||
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
if (showTime) {
|
||||||
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span><a class='dx-link' href='https://qrz.com/db/${s["dx_call"]}' target='_new'>${s["dx_call"]}</a></td>`);
|
$tr.append(`<td class='nowrap'>${time_formatted}</td>`);
|
||||||
$tr.append(`<td class='nowrap'><span class='band-bullet band-bullet-${cssFormattedBandName}' title='${bandFullName}'>■</span>${freq_string}</td>`);
|
}
|
||||||
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
if (showDX) {
|
||||||
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
$tr.append(`<td class='nowrap'><span class='flag-wrapper hideonmobile' title='${dx_country}'>${dx_flag}</span><a class='dx-link' href='https://qrz.com/db/${s["dx_call"]}' target='_new'>${s["dx_call"]}</a></td>`);
|
||||||
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${sigSourceText}</td>`);
|
}
|
||||||
$tr.append(`<td class='hideonmobile'><span ${sig_refs_title_string}>${sig_refs}</span></td>`);
|
if (showFreq) {
|
||||||
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
$tr.append(`<td class='nowrap'><span class='band-bullet band-bullet-${cssFormattedBandName}' title='${bandFullName}'>■</span>${freq_string}</td>`);
|
||||||
|
}
|
||||||
|
if (showMode) {
|
||||||
|
$tr.append(`<td class='nowrap'>${mode_string}</td>`);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
$tr.append(`<td class='hideonmobile'>${commentText}</td>`);
|
||||||
|
}
|
||||||
|
if (showSource) {
|
||||||
|
$tr.append(`<td class='nowrap hideonmobile'><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${sigSourceText}</td>`);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
$tr.append(`<td class='hideonmobile'><span ${sig_refs_title_string}>${sig_refs}</span></td>`);
|
||||||
|
}
|
||||||
|
if (showDE) {
|
||||||
|
$tr.append(`<td class='nowrap hideonmobile'><span class='flag-wrapper' title='${de_country}'>${de_flag}</span>${de_call}</td>`);
|
||||||
|
}
|
||||||
table.find('tbody').append($tr);
|
table.find('tbody').append($tr);
|
||||||
|
|
||||||
// Second row for mobile view only, containing source, ref & comment
|
// Second row for mobile view only, containing source, ref & comment
|
||||||
@@ -157,7 +203,17 @@ function updateTable() {
|
|||||||
if (s["qrt"] == true) {
|
if (s["qrt"] == true) {
|
||||||
$tr2.addClass("table-faded");
|
$tr2.addClass("table-faded");
|
||||||
}
|
}
|
||||||
$tr2.append(`<td colspan="100"><span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> ${sig_refs} ${commentText}</td>`);
|
$td2 = $("<td colspan='100'>");
|
||||||
|
if (showSource) {
|
||||||
|
$td2.append(`<span class='icon-wrapper'><i class='fa-solid fa-${s["icon"]}'></i></span> `);
|
||||||
|
}
|
||||||
|
if (showRef) {
|
||||||
|
$td2.append(`${sig_refs} `);
|
||||||
|
}
|
||||||
|
if (showComment) {
|
||||||
|
$td2.append(`${commentText} `);
|
||||||
|
}
|
||||||
|
$tr2.append($td2);
|
||||||
table.find('tbody').append($tr2);
|
table.find('tbody').append($tr2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,9 +238,17 @@ function loadOptions() {
|
|||||||
$("#filters-container-2").append(generateMultiToggleFilterCard("Modes", "mode_type", options["mode_types"]));
|
$("#filters-container-2").append(generateMultiToggleFilterCard("Modes", "mode_type", options["mode_types"]));
|
||||||
$("#filters-container-2").append(generateMultiToggleFilterCard("Sources", "source", options["spot_sources"]));
|
$("#filters-container-2").append(generateMultiToggleFilterCard("Sources", "source", options["spot_sources"]));
|
||||||
|
|
||||||
// Load filters from settings storage
|
// Load settings from settings storage now all the controls are available
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
|
// Extra setting - the toggle for the "bearing" column is disabled if the user has not entered a valid grid, and
|
||||||
|
// normally this logic is handled on user input to the grid field, but we might have just loaded a value direct
|
||||||
|
// into the field, so apply the same logic here.
|
||||||
|
$("#tableShowBearing").prop('disabled', !isUserGridValid());
|
||||||
|
if (!isUserGridValid()) {
|
||||||
|
$("#tableShowBearing").prop('checked', false);
|
||||||
|
}
|
||||||
|
|
||||||
// Load spots and set up the timer
|
// Load spots and set up the timer
|
||||||
loadSpots();
|
loadSpots();
|
||||||
setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000);
|
setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000);
|
||||||
@@ -228,12 +292,33 @@ function generateBandsMultiToggleFilterCard(band_options) {
|
|||||||
return $col;
|
return $col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Work out if the user's entered grid is a valid Maidenhead grid
|
||||||
|
function isUserGridValid() {
|
||||||
|
userGrid = $("#userGrid").val().toUpperCase();
|
||||||
|
return latLonForGridCentre(userGrid) != null;
|
||||||
|
}
|
||||||
|
|
||||||
// Method called when any filter is changed to reload the spots and persist the filter settings.
|
// Method called when any filter is changed to reload the spots and persist the filter settings.
|
||||||
function filtersUpdated() {
|
function filtersUpdated() {
|
||||||
loadSpots();
|
loadSpots();
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method called when the user's grid input is changed.
|
||||||
|
function userGridUpdated() {
|
||||||
|
var userGridValid = isUserGridValid();
|
||||||
|
if (userGridValid) {
|
||||||
|
updateTable();
|
||||||
|
}
|
||||||
|
// Enable/disable bearing column depending on grid validity
|
||||||
|
$("#tableShowBearing").prop('disabled', !userGridValid);
|
||||||
|
if (!userGridValid) {
|
||||||
|
$("#tableShowBearing").prop('checked', false);
|
||||||
|
}
|
||||||
|
// Save settings even if not a valid grid, this allows the user to clear their grid and have it save.
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
// React to toggling/closing panels
|
// React to toggling/closing panels
|
||||||
function toggleFiltersPanel() {
|
function toggleFiltersPanel() {
|
||||||
// If we are going to display the filters panel, hide the display panel
|
// If we are going to display the filters panel, hide the display panel
|
||||||
|
|||||||
Reference in New Issue
Block a user