diff --git a/core/utils.py b/core/utils.py index d4e8e30..7053338 100644 --- a/core/utils.py +++ b/core/utils.py @@ -113,6 +113,7 @@ def infer_country_from_callsign(call): # Infer a DXCC ID from a callsign def infer_dxcc_id_from_callsign(call): + get_clublog_xml_data_for_callsign("M0TRT") try: # Start with the basic country-files.com-based decoder. 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 return data 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 else: 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 return data 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 except APIKeyMissingError: # 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) return data 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 else: return None @@ -310,11 +314,21 @@ def infer_latlon_from_callsign_dxcc(call): try: data = CALL_INFO_BASIC.get_lat_long(call) if data and "latitude" in data and "longitude" in data: - return [data["latitude"], data["longitude"]] + loc = [data["latitude"], data["longitude"]] else: - return None + loc = None 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) diff --git a/views/webpage_alerts.tpl b/views/webpage_alerts.tpl index 54e41e7..4f7a553 100644 --- a/views/webpage_alerts.tpl +++ b/views/webpage_alerts.tpl @@ -80,31 +80,31 @@
| ${useLocalTime ? "Start (Local)" : "Start UTC"} | `); - table.find('thead tr').append(`${useLocalTime ? "End (Local)" : "End UTC"} | `); - table.find('thead tr').append(`DX | `); - table.find('thead tr').append(`Frequencies & Modes | `); - table.find('thead tr').append(`Comment | `); - table.find('thead tr').append(`Source | `); - table.find('thead tr').append(`Ref. | `); + if (showStartTime) { + table.find('thead tr').append(`${useLocalTime ? "Start (Local)" : "Start UTC"} | `); + } + if (showEndTime) { + table.find('thead tr').append(`${useLocalTime ? "End (Local)" : "End UTC"} | `); + } + if (showDX) { + table.find('thead tr').append(`DX | `); + } + if (showFreqsModes) { + table.find('thead tr').append(`Frequencies & Modes | `); + } + if (showComment) { + table.find('thead tr').append(`Comment | `); + } + if (showSource) { + table.find('thead tr').append(`Source | `); + } + if (showRef) { + table.find('thead tr').append(`Ref. | `); + } // 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${end_time_formatted} | `); - $tr.append(`${dx_calls_html}${dx_country_html} | `); - $tr.append(`${freqsModesText} | `); - $tr.append(`${commentText} | `); - $tr.append(``); - $tr.append(`${sig_refs} | `); + if (showStartTime) { + $tr.append(`${start_time_formatted} | `); + } + if (showEndTime) { + $tr.append(`${end_time_formatted} | `); + } + if (showDX) { + $tr.append(`${dx_calls_html}${dx_country_html} | `); + } + if (showFreqsModes) { + $tr.append(`${freqsModesText} | `); + } + if (showComment) { + $tr.append(`${commentText} | `); + } + if (showSource) { + $tr.append(``); + } + if (showRef) { + $tr.append(`${sig_refs} | `); + } tbody.append($tr); // Second row for mobile view only, containing source, ref, freqs/modes & comment $tr2 = $("|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ${sig_refs} ${freqsModesText} ${commentText} | `);
+ $td2 = $("");
+ if (showSource) {
+ $td2.append(` `);
+ }
+ if (showRef) {
+ $td2.append(`${sig_refs} `);
+ }
+ if (showFreqsModes) {
+ $td2.append(`${freqsModesText} `);
+ }
+ if (showComment) {
+ $td2.append(` ${commentText} `); + } + $tr2.append($td2); tbody.append($tr2); }); } diff --git a/webassets/js/common.js b/webassets/js/common.js index 513ede4..8c0312d 100644 --- a/webassets/js/common.js +++ b/webassets/js/common.js @@ -103,6 +103,97 @@ function timeZoneUpdated() { 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 function saveSettings() { // Find all storeable UI elements, store a key of "element id:property name" mapped to the value of that diff --git a/webassets/js/spots.js b/webassets/js/spots.js index 623cf81..22e8bf5 100644 --- a/webassets/js/spots.js +++ b/webassets/js/spots.js @@ -34,16 +34,46 @@ function updateTable() { // Use local time instead of UTC? 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 let table = $('
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||