mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2025-10-27 08:49:27 +00:00
Start work on bands display. #48
This commit is contained in:
@@ -39,6 +39,7 @@ class WebServer:
|
|||||||
# Routes for templated pages
|
# Routes for templated pages
|
||||||
bottle.get("/")(lambda: self.serve_template('webpage_spots'))
|
bottle.get("/")(lambda: self.serve_template('webpage_spots'))
|
||||||
bottle.get("/map")(lambda: self.serve_template('webpage_map'))
|
bottle.get("/map")(lambda: self.serve_template('webpage_map'))
|
||||||
|
bottle.get("/bands")(lambda: self.serve_template('webpage_bands'))
|
||||||
bottle.get("/alerts")(lambda: self.serve_template('webpage_alerts'))
|
bottle.get("/alerts")(lambda: self.serve_template('webpage_alerts'))
|
||||||
bottle.get("/status")(lambda: self.serve_template('webpage_status'))
|
bottle.get("/status")(lambda: self.serve_template('webpage_status'))
|
||||||
bottle.get("/about")(lambda: self.serve_template('webpage_about'))
|
bottle.get("/about")(lambda: self.serve_template('webpage_about'))
|
||||||
@@ -224,6 +225,16 @@ class WebServer:
|
|||||||
case "comment_includes":
|
case "comment_includes":
|
||||||
comment_includes = query.get(k).strip()
|
comment_includes = query.get(k).strip()
|
||||||
spots = [s for s in spots if s.comment and comment_includes.upper() in s.comment.upper()]
|
spots = [s for s in spots if s.comment and comment_includes.upper() in s.comment.upper()]
|
||||||
|
case "allow_qrt":
|
||||||
|
# If false, spots that are flagged as QRT are not returned.
|
||||||
|
prevent_qrt = query.get(k).upper() == "FALSE"
|
||||||
|
if prevent_qrt:
|
||||||
|
spots = [s for s in spots if not s.qrt or s.qrt == False]
|
||||||
|
case "needs_location":
|
||||||
|
# If true, spots require a location to be returned
|
||||||
|
needs_location = query.get(k).upper() == "TRUE"
|
||||||
|
if needs_location:
|
||||||
|
spots = [s for s in spots if s.latitude and s.longitude]
|
||||||
case "dedupe":
|
case "dedupe":
|
||||||
# Ensure only the latest spot of each callsign is present in the list. This relies on the list being
|
# Ensure only the latest spot of each callsign is present in the list. This relies on the list being
|
||||||
# in reverse time order, so if any future change allows re-ordering the list, that should be done
|
# in reverse time order, so if any future change allows re-ordering the list, that should be done
|
||||||
|
|||||||
124
views/webpage_bands.tpl
Normal file
124
views/webpage_bands.tpl
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
% rebase('webpage_base.tpl')
|
||||||
|
|
||||||
|
<div id="intro-box" class="mt-3">
|
||||||
|
<div class="alert alert-primary alert-dismissible fade show" role="alert">
|
||||||
|
<i class="fa-solid fa-circle-info"></i> <strong>What is Spothole?</strong><br/>Spothole is an aggregator of amateur radio spots from DX clusters and outdoor activity programmes. It's free for anyone to use and includes an API that developers can build other applications on. For more information, check out the <a href="/about" class="alert-link">"About" page</a>. If that sounds like nonsense to you, you can visit <a href="/about#faq" class="alert-link">the FAQ section</a> to learn more.
|
||||||
|
<button type="button" id="intro-box-dismiss" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto me-auto pt-3">
|
||||||
|
<p id="timing-container">Loading...</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<p class="d-inline-flex gap-1">
|
||||||
|
<button id="filters-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleFiltersPanel();"><i class="fa-solid fa-filter"></i> Filters</button>
|
||||||
|
<button id="display-button" type="button" class="btn btn-outline-primary" data-bs-toggle="button" onclick="toggleDisplayPanel();"><i class="fa-solid fa-desktop"></i> Display</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="filters-area" class="appearing-panel card mb-3">
|
||||||
|
<div class="card-header text-white bg-primary">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto me-auto">
|
||||||
|
Filters
|
||||||
|
</div>
|
||||||
|
<div class="col-auto d-inline-flex">
|
||||||
|
<button id="close-filters-button" type="button" class="btn-close btn-close-white" aria-label="Close" onclick="closeFiltersPanel();"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row row-cols-1 g-4 mb-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Bands</h5>
|
||||||
|
<p id="band-options" class="card-text spothole-card-text"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-1 row-cols-md-4 g-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">DX Continent</h5>
|
||||||
|
<p id="dx-continent-options" class="card-text spothole-card-text"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">DE Continent</h5>
|
||||||
|
<p id="de-continent-options" class="card-text spothole-card-text"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Modes</h5>
|
||||||
|
<p id="mode-options" class="card-text spothole-card-text"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Sources</h5>
|
||||||
|
<p id="source-options" class="card-text spothole-card-text"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="display-area" class="appearing-panel card mb-3">
|
||||||
|
<div class="card-header text-white bg-primary">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto me-auto">
|
||||||
|
Display
|
||||||
|
</div>
|
||||||
|
<div class="col-auto d-inline-flex">
|
||||||
|
<button id="close-display-button" type="button" class="btn-close btn-close-white" aria-label="Close" onclick="closeDisplayPanel();"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="display-container" class="row row-cols-1 row-cols-md-4 g-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Spot Age</h5>
|
||||||
|
<p class="card-text spothole-card-text">Last
|
||||||
|
<select id="max-spot-age" class="storeable-select form-select ms-2 me-2 d-inline-block" oninput="filtersUpdated();" style="width: 5em; display: inline-block;">
|
||||||
|
<option value="300">5</option>
|
||||||
|
<option value="600">10</option>
|
||||||
|
<option value="1800" selected>30</option>
|
||||||
|
<option value="3600">60</option>
|
||||||
|
</select>
|
||||||
|
minutes
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bands-container"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/js/common.js"></script>
|
||||||
|
<script src="/js/spotandmap.js"></script>
|
||||||
|
<script src="/js/bands.js"></script>
|
||||||
|
<script>$(document).ready(function() { $("#nav-link-bands").addClass("active"); }); <!-- highlight active page in nav --></script>
|
||||||
@@ -59,6 +59,7 @@
|
|||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<li class="nav-item ms-4"><a href="/" class="nav-link" id="nav-link-spots"><i class="fa-solid fa-tower-cell"></i> Spots</a></li>
|
<li class="nav-item ms-4"><a href="/" class="nav-link" id="nav-link-spots"><i class="fa-solid fa-tower-cell"></i> Spots</a></li>
|
||||||
<li class="nav-item ms-4"><a href="/map" class="nav-link" id="nav-link-map"><i class="fa-solid fa-map"></i> Map</a></li>
|
<li class="nav-item ms-4"><a href="/map" class="nav-link" id="nav-link-map"><i class="fa-solid fa-map"></i> Map</a></li>
|
||||||
|
<li class="nav-item ms-4"><a href="/bands" class="nav-link" id="nav-link-bands"><i class="fa-solid fa-ruler-vertical"></i> Bands</a></li>
|
||||||
<li class="nav-item ms-4"><a href="/alerts" class="nav-link" id="nav-link-alerts"><i class="fa-solid fa-bell"></i> Alerts</a></li>
|
<li class="nav-item ms-4"><a href="/alerts" class="nav-link" id="nav-link-alerts"><i class="fa-solid fa-bell"></i> Alerts</a></li>
|
||||||
<li class="nav-item ms-4"><a href="/status" class="nav-link" id="nav-link-status"><i class="fa-solid fa-chart-simple"></i> Status</a></li>
|
<li class="nav-item ms-4"><a href="/status" class="nav-link" id="nav-link-status"><i class="fa-solid fa-chart-simple"></i> Status</a></li>
|
||||||
<li class="nav-item ms-4"><a href="/about" class="nav-link" id="nav-link-about"><i class="fa-solid fa-circle-info"></i> About</a></li>
|
<li class="nav-item ms-4"><a href="/about" class="nav-link" id="nav-link-about"><i class="fa-solid fa-circle-info"></i> About</a></li>
|
||||||
|
|||||||
@@ -195,6 +195,20 @@ paths:
|
|||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- name: needs_location
|
||||||
|
in: query
|
||||||
|
description: Return only spots with a location set.
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
- name: allow_qrt
|
||||||
|
in: query
|
||||||
|
description: Allow spots that are known to be QRT to be returned.
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Success
|
description: Success
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ div#map {
|
|||||||
.hideonmobile {
|
.hideonmobile {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
div#map, div#table-container {
|
div#map, div#table-container, div#bands-container {
|
||||||
margin-left: -1em;
|
margin-left: -1em;
|
||||||
margin-right: -1em;
|
margin-right: -1em;
|
||||||
}
|
}
|
||||||
|
|||||||
199
webassets/js/bands.js
Normal file
199
webassets/js/bands.js
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Load spots and populate the bands display.
|
||||||
|
function loadSpots() {
|
||||||
|
$.getJSON('/api/v1/spots' + buildQueryString(), function(jsonData) {
|
||||||
|
// Store last updated time
|
||||||
|
lastUpdateTime = moment.utc();
|
||||||
|
updateRefreshDisplay();
|
||||||
|
// Store data
|
||||||
|
spots = jsonData;
|
||||||
|
// Update bands display
|
||||||
|
updateBands();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a query string for the API, based on the filters that the user has selected.
|
||||||
|
function buildQueryString() {
|
||||||
|
var str = "?";
|
||||||
|
["dx_continent", "de_continent", "mode_type", "source", "band"].forEach(fn => {
|
||||||
|
if (!allFilterOptionsSelected(fn)) {
|
||||||
|
str = str + getQueryStringFor(fn) + "&";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
str = str + "max_age=" + $("#max-spot-age option:selected").val();
|
||||||
|
// Additional filters for the bands view: No dupes, no QRT
|
||||||
|
str = str + "&dedupe=true&allow_qrt=false";
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the bands display
|
||||||
|
function updateBands() {
|
||||||
|
// Stop here if nothing to display
|
||||||
|
var bandsPanel = $("#bands-container");
|
||||||
|
if (spots.length === 0) {
|
||||||
|
// todo bootstrapify
|
||||||
|
bandsPanel.html("<p id='bandPanelNoSpots'>There are no spots matching your filters.</p>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do some harsher de-duping. Because we only display callsign, frequency and mode here, the previous
|
||||||
|
// de-duplication could have let some through that don't look like dupes on the map, but would do here.
|
||||||
|
// Typically that's a person activating two programs at the same time, e.g. POTA & WWFF.
|
||||||
|
// todo fix from here
|
||||||
|
spotList = removeDuplicatesForBandPanel(spotList);
|
||||||
|
|
||||||
|
// Convert to a map of band names to the spots on that band. Bands with no
|
||||||
|
// spots in view will not be present.
|
||||||
|
const bandToSpots = new Map();
|
||||||
|
options["bands"].forEach(function (band) {
|
||||||
|
const matchingSpots = spots.filter(function (s) {
|
||||||
|
return s.band === band.name;
|
||||||
|
});
|
||||||
|
if (matchingSpots.length > 0) {
|
||||||
|
bandToSpots.set(band.name, matchingSpots);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build up HTML content for each band
|
||||||
|
let html = "";
|
||||||
|
const columnWidthPercent = Math.max(30, 100 / bandToSpots.size);
|
||||||
|
let columnIndex = 0;
|
||||||
|
bandToSpots.forEach(function (spotList, bandName) {
|
||||||
|
// Get the colours for the band from the first spot, and prepare the header
|
||||||
|
html += "<div class='bandCol' style='width:" + columnWidthPercent + "%'>";
|
||||||
|
html += "<div class='bandColHeader' style='background-color:" + spotList[0].color + "; color:" + spotList[0].contrast_color + "'>" + spotList[0].band + "</div>";
|
||||||
|
html += "<div class='bandColMiddle'>";
|
||||||
|
|
||||||
|
// Get the band data to fetch start and end frequencies
|
||||||
|
let band = options["bands"].filter(function (b) {
|
||||||
|
return b.name === bandName;
|
||||||
|
})[0];
|
||||||
|
// Start printing the band
|
||||||
|
const freqStep = (band.stopFreq - band.startFreq) / 40.0;
|
||||||
|
html += "<ul>";
|
||||||
|
html += "<li><span>-</span></li>";
|
||||||
|
|
||||||
|
// Do 40 steps down the band
|
||||||
|
for (let i = 0; i <= 40; i++) {
|
||||||
|
|
||||||
|
// Work out if there are any spots in this step
|
||||||
|
const freqStepStart = band.startFreq + i * freqStep;
|
||||||
|
const freqStepEnd = freqStepStart + freqStep;
|
||||||
|
const spotsInStep = spotList.filter(function (s) {
|
||||||
|
// Normally we do >= start and < end, but in the special case where this is the last step and there is a spot
|
||||||
|
// right at the end of the band, we include this too
|
||||||
|
return s.freq >= freqStepStart && (s.freq < freqStepEnd || (s.freq === freqStepEnd && freqStepEnd === band.stopFreq));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (spotsInStep.length > 0) {
|
||||||
|
// If this step has spots in it, print them
|
||||||
|
html += "<li class='withSpots'><span>";
|
||||||
|
spotsInStep.sort((a, b) => (a.freq > b.freq) ? 1 : ((b.freq > a.freq) ? -1 : 0));
|
||||||
|
spotsInStep.forEach(function (s) {
|
||||||
|
// Figure out the class to use for the spot's div, which defines its colour.
|
||||||
|
let spotDivClass = "bandColSpotCurrent";
|
||||||
|
if (currentPopupSpotUID === s.uid) {
|
||||||
|
spotDivClass = "bandColSpotSelected";
|
||||||
|
} else if (preQSYStatusShouldShowGrey(s.preqsy)) {
|
||||||
|
spotDivClass = "bandColSpotOld";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += "<div class='bandColSpot " + spotDivClass + "' onClick='handleBandPanelSpotClick(\"" + s.uid + "\")'><span class='bandColSpot'>" + s.activator + "<br/><span class='bandColSpotFreq'>" + getFormattedFrequency(s.freq) + "</span>";
|
||||||
|
if (s.mode != null && s.mode.length > 0 && s.mode !== "Unknown") {
|
||||||
|
html += "<span class='bandColSpotMode'>" + s.mode + "</span>";
|
||||||
|
}
|
||||||
|
html += "</span></div>";
|
||||||
|
});
|
||||||
|
html += "</li></span>";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Step had no spots in it, so just print a marker. This is a frequency on multiples of 4, or a dash otherwise.
|
||||||
|
if (i % 4 === 0) {
|
||||||
|
html += "<li><span>—" + (band.startFreq + i * freqStep).toFixed(3) + "</span></li>";
|
||||||
|
} else if (i % 4 === 2) {
|
||||||
|
html += "<li><span>–</span></li>";
|
||||||
|
} else {
|
||||||
|
html += "<li><span>-</span></li>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html += "<li><span>-</span></li>";
|
||||||
|
html += "</ul>";
|
||||||
|
|
||||||
|
html += "</div></div>";
|
||||||
|
columnIndex++;
|
||||||
|
});
|
||||||
|
// Update the DOM with the band HTML
|
||||||
|
bandsPanel.html(html);
|
||||||
|
|
||||||
|
// Desktop mouse wheel to scroll bands horizontally if used on the headers
|
||||||
|
// noinspection JSDeprecatedSymbols
|
||||||
|
$(".bandColHeader").on("wheel", () => bandsPanel.scrollLeft(bandsPanel.scrollLeft() + event.deltaY / 10.0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
$.getJSON('/api/v1/options', function(jsonData) {
|
||||||
|
// Store options
|
||||||
|
options = jsonData;
|
||||||
|
|
||||||
|
// Add CSS for band toggle buttons
|
||||||
|
addBandToggleColourCSS(options["bands"]);
|
||||||
|
|
||||||
|
// Populate the filters panel
|
||||||
|
generateBandsMultiToggleFilterCard(options["bands"]);
|
||||||
|
generateMultiToggleFilterCard("#dx-continent-options", "dx_continent", options["continents"]);
|
||||||
|
generateMultiToggleFilterCard("#de-continent-options", "de_continent", options["continents"]);
|
||||||
|
generateMultiToggleFilterCard("#mode-options", "mode_type", options["mode_types"]);
|
||||||
|
generateMultiToggleFilterCard("#source-options", "source", options["spot_sources"]);
|
||||||
|
|
||||||
|
// Load settings from settings storage now all the controls are available
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
|
// Load spots and set up the timer
|
||||||
|
loadSpots();
|
||||||
|
setInterval(loadSpots, REFRESH_INTERVAL_SEC * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method called when any display property is changed to reload the map and persist the display settings.
|
||||||
|
function displayUpdated() {
|
||||||
|
updateMap();
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// React to toggling/closing panels
|
||||||
|
function toggleFiltersPanel() {
|
||||||
|
// If we are going to show the filters panel, hide the display panel
|
||||||
|
if (!$("#filters-area").is(":visible") && $("#display-area").is(":visible")) {
|
||||||
|
$("#display-area").hide();
|
||||||
|
$("#display-button").button("toggle");
|
||||||
|
}
|
||||||
|
$("#filters-area").toggle();
|
||||||
|
}
|
||||||
|
function closeFiltersPanel() {
|
||||||
|
$("#filters-button").button("toggle");
|
||||||
|
$("#filters-area").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDisplayPanel() {
|
||||||
|
// If we are going to show the display panel, hide the filters panel
|
||||||
|
if (!$("#display-area").is(":visible") && $("#filters-area").is(":visible")) {
|
||||||
|
$("#filters-area").hide();
|
||||||
|
$("#filters-button").button("toggle");
|
||||||
|
}
|
||||||
|
$("#display-area").toggle();
|
||||||
|
}
|
||||||
|
function closeDisplayPanel() {
|
||||||
|
$("#display-button").button("toggle");
|
||||||
|
$("#display-area").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Call loadOptions(), this will then trigger loading spots and setting up timers.
|
||||||
|
loadOptions();
|
||||||
|
// Update the refresh timing display every second
|
||||||
|
setInterval(updateRefreshDisplay, 1000);
|
||||||
|
});
|
||||||
@@ -3,7 +3,7 @@ var markersLayer;
|
|||||||
var geodesicsLayer;
|
var geodesicsLayer;
|
||||||
var terminator;
|
var terminator;
|
||||||
|
|
||||||
// Load spots and populate the table.
|
// Load spots and populate the map.
|
||||||
function loadSpots() {
|
function loadSpots() {
|
||||||
$.getJSON('/api/v1/spots' + buildQueryString(), function(jsonData) {
|
$.getJSON('/api/v1/spots' + buildQueryString(), function(jsonData) {
|
||||||
// Store data
|
// Store data
|
||||||
@@ -23,6 +23,8 @@ function buildQueryString() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
str = str + "max_age=" + $("#max-spot-age option:selected").val();
|
str = str + "max_age=" + $("#max-spot-age option:selected").val();
|
||||||
|
// Additional filters for the map view: No dupes, no QRT, only spots with locations
|
||||||
|
str = str + "&dedupe=true&allow_qrt=false&needs_location=true";
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,29 +34,20 @@ function updateMap() {
|
|||||||
markersLayer.clearLayers();
|
markersLayer.clearLayers();
|
||||||
geodesicsLayer.clearLayers();
|
geodesicsLayer.clearLayers();
|
||||||
|
|
||||||
// Make new markers for all spots with a good location, not QRT, and not a duplicate spot within the data set.
|
// Make new markers for all spots that match the filter
|
||||||
var callsAlreadyDisplayed = [];
|
|
||||||
spots.forEach(function (s) {
|
spots.forEach(function (s) {
|
||||||
if (s["dx_location_good"] && (s["qrt"] == null || s["qrt"] == false)) {
|
var m = L.marker([s["dx_latitude"], s["dx_longitude"]], {icon: getIcon(s)});
|
||||||
if (!callsAlreadyDisplayed.includes(s["dx_call"])) {
|
m.bindPopup(getTooltipText(s));
|
||||||
|
markersLayer.addLayer(m);
|
||||||
|
|
||||||
// OK, create the marker
|
// Create geodesics if required
|
||||||
var m = L.marker([s["dx_latitude"], s["dx_longitude"]], {icon: getIcon(s)});
|
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
|
||||||
m.bindPopup(getTooltipText(s));
|
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
||||||
markersLayer.addLayer(m);
|
color: s["band_color"],
|
||||||
|
wrap: false,
|
||||||
// Create geodesics if required
|
steps: 5
|
||||||
if ($("#mapShowGeodesics")[0].checked && s["de_latitude"] != null && s["de_longitude"] != null) {
|
});
|
||||||
var geodesic = L.geodesic([[s["de_latitude"], s["de_longitude"]], m.getLatLng()], {
|
geodesicsLayer.addLayer(geodesic);
|
||||||
color: s["band_color"],
|
|
||||||
wrap: false,
|
|
||||||
steps: 5
|
|
||||||
});
|
|
||||||
geodesicsLayer.addLayer(geodesic);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
callsAlreadyDisplayed.push(s["dx_call"]);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user