diff --git a/README.md b/README.md index 031c599..622cfb7 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ Supported data sources include DX Clusters, the Reverse Beacon Network (RBN), th You can access the public version's web interface at [https://spothole.app](https://spothole.app), and see [https://spothole.app/apidocs](https://spothole.app/apidocs) for the API details. -Please note this URL is not necessarily final, and the API is likely to change as the project works its way towards v1.0. Please do not build anything on the Spothole API yet! - ### Running your own copy To download and set up Spothole on a Debian server, run the following commands. Other operating systems will likely be similar. diff --git a/core/constants.py b/core/constants.py index 8b7f964..f581033 100644 --- a/core/constants.py +++ b/core/constants.py @@ -3,7 +3,7 @@ from data.band import Band # General software SOFTWARE_NAME = "Spothole by M0TRT" -SOFTWARE_VERSION = "1.0" +SOFTWARE_VERSION = "0.1" # HTTP headers used for spot providers that use HTTP HTTP_HEADERS = {"User-Agent": SOFTWARE_NAME + " " + SOFTWARE_VERSION + " (operated by " + SERVER_OWNER_CALLSIGN + ")"} diff --git a/data/spot.py b/data/spot.py index 0b3c4ee..ea714fb 100644 --- a/data/spot.py +++ b/data/spot.py @@ -103,8 +103,17 @@ class Spot: sig_refs_names: list = None # Activation score. SOTA only 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. + + + # Display guidance (optional) + + # 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" + # Colour to represent this spot, if a client chooses to colour spots based on their frequency band, using PSK + # Reporter's default colours. HTML colour e.g. hex. A contrast colour is also provided which will be black or white. + band_color: str = None + band_contrast_color: str = None # Timing info @@ -183,6 +192,8 @@ class Spot: if self.freq and not self.band: band = lookup_helper.infer_band_from_freq(self.freq) self.band = band.name + self.band_color = band.color + self.band_contrast_color = band.contrast_color # Mode from comments or bandplan if self.mode: diff --git a/spothole.py b/spothole.py index 71b1259..6386a59 100644 --- a/spothole.py +++ b/spothole.py @@ -7,7 +7,8 @@ import sys from diskcache import Cache from core.cleanup import CleanupTimer -from core.config import config, WEB_SERVER_PORT +from core.config import config, WEB_SERVER_PORT, SERVER_OWNER_CALLSIGN +from core.constants import SOFTWARE_NAME, SOFTWARE_VERSION from core.lookup_helper import lookup_helper from core.status_reporter import StatusReporter from server.webserver import WebServer @@ -62,6 +63,8 @@ if __name__ == '__main__': root.addHandler(handler) logging.info("Starting...") + logging.info( + "This is " + SOFTWARE_NAME + " version " + SOFTWARE_VERSION + ". This instance is run by " + SERVER_OWNER_CALLSIGN + ".") # Shut down gracefully on SIGINT signal.signal(signal.SIGINT, shutdown) @@ -90,7 +93,7 @@ if __name__ == '__main__': cleanup_timer.start() # Set up web server - web_server = WebServer(spots=spots, alerts= alerts, status_data=status_data, port=WEB_SERVER_PORT) + web_server = WebServer(spots=spots, alerts=alerts, status_data=status_data, port=WEB_SERVER_PORT) web_server.start() # Set up status reporter diff --git a/webassets/apidocs/openapi.yml b/webassets/apidocs/openapi.yml index b0c8e7a..a14e8e6 100644 --- a/webassets/apidocs/openapi.yml +++ b/webassets/apidocs/openapi.yml @@ -827,6 +827,14 @@ 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 + band_color: + type: string + descripton: Colour to represent this spot, if a client chooses to colour spots based on their frequency band, using PSK Reporter's default colours. HTML colour e.g. hex. + example: #ff0000" + band_contrast_color: + type: string + descripton: Black or white, whichever best contrasts with "band_color". + example: "white" source: type: string description: Where we got the alert from. diff --git a/webassets/css/style.css b/webassets/css/style.css index 8368778..0037c6e 100644 --- a/webassets/css/style.css +++ b/webassets/css/style.css @@ -142,6 +142,12 @@ div#map { font-family: var(--bs-body-font-family) !important; } +a.leaflet-popup-callsign-link { + color: black; + font-weight: bold; + text-decoration: none; +} + /* GENERAL MOBILE SUPPORT */ diff --git a/webassets/js/map.js b/webassets/js/map.js index d57aff2..b4923b8 100644 --- a/webassets/js/map.js +++ b/webassets/js/map.js @@ -33,6 +33,7 @@ function updateMap() { spots.forEach(function (s) { if (s["dx_location_good"]) { let m = L.marker([s["dx_latitude"], s["dx_longitude"]], {icon: getIcon(s)}); + m.bindPopup(getTooltipText(s)); markersLayer.addLayer(m); } }); @@ -42,14 +43,75 @@ function updateMap() { function getIcon(s) { return L.ExtraMarkers.icon({ icon: "fa-" + s["icon"], - iconColor: "white", // todo - markerColor: "black", // todo + iconColor: s["band_contrast_color"], + markerColor: s["band_color"], shape: 'circle', prefix: 'fa', svg: true }); } +// Tooltip text for the markers +function getTooltipText(s) { + // Format DX flag + var dx_flag = ""; + if (s["dx_flag"] && s["dx_flag"] != null && s["dx_flag"] != "") { + dx_flag = s["dx_flag"]; + } + + // Format the frequency + var mhz = Math.floor(s["freq"] / 1000000.0); + var khz = Math.floor((s["freq"] - (mhz * 1000000.0)) / 1000.0); + var hz = Math.floor(s["freq"] - (mhz * 1000000.0) - (khz * 1000.0)); + var hz_string = (hz > 0) ? hz.toFixed(0)[0] : ""; + var freq_string = `${mhz.toFixed(0)}${khz.toFixed(0).padStart(3, '0')}${hz_string}` + + // Format comment + var commentText = ""; + if (s["comment"] != null) { + commentText = escapeHtml(s["comment"]); + } + + // Sig or fallback to source + var sigSourceText = s["source"]; + if (s["sig"]) { + sigSourceText = s["sig"]; + } + + // Format sig_refs + var sig_refs = ""; + if (s["sig_refs"]) { + sig_refs = s["sig_refs"].map(s => `${s}`).join(", "); + } + + // DX + const shortCall = s["dx_call"].split("/").sort(function (a, b) { + return b.length - a.length; + })[0]; + ttt = `${dx_flag} ${s["dx_call"]}
`; + + // Frequency & band + ttt += ` ${freq_string} (${s["band"]})`; + // Mode + if (s["mode"] != null) { + ttt += `     ${s["mode"]}`; + } + ttt += "
"; + + // Source / SIG / Ref + ttt += ` ${sigSourceText} ${sig_refs}
`; + + // Time + ttt += ` ${moment.unix(s["time"]).fromNow()}`; + + // Comment + if (commentText.length > 0) { + ttt += `
${commentText}`; + } + + return ttt; +} + // Load server options. Once a successful callback is made from this, we then query spots and set up the timer to query // spots repeatedly. function loadOptions() { @@ -57,8 +119,8 @@ function loadOptions() { // Store options options = jsonData; - // Add CSS for band bullets and band toggle buttons - addBandColourCSS(options["bands"]); + // Add CSS for band toggle buttons + addBandToggleColourCSS(options["bands"]); // Populate the filters panel generateBandsMultiToggleFilterCard(options["bands"]); diff --git a/webassets/js/spotandmap.js b/webassets/js/spotandmap.js index 8021a5d..58a8c37 100644 --- a/webassets/js/spotandmap.js +++ b/webassets/js/spotandmap.js @@ -4,14 +4,13 @@ const REFRESH_INTERVAL_SEC = 60; // Storage for the spot data that the server gives us. var spots = [] -// Dynamically add CSS code for the band bullets and band toggle buttons to be in the appropriate colour. +// Dynamically add CSS code for the band toggle buttons to be in the appropriate colour. // Some band names contain decimal points which are not allowed in CSS classes, so we text-replace them to "p". -function addBandColourCSS(band_options) { +function addBandToggleColourCSS(band_options) { var $style = $('