diff --git a/application/controllers/Logbookadvanced.php b/application/controllers/Logbookadvanced.php
index d78fb41de..d6c994d82 100644
--- a/application/controllers/Logbookadvanced.php
+++ b/application/controllers/Logbookadvanced.php
@@ -100,6 +100,7 @@ class Logbookadvanced extends CI_Controller {
'assets/js/leaflet/geocoding.js',
'assets/js/globe/globe.gl.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/globe/globe.gl.js")),
'assets/js/bootstrap-multiselect.js?' . filemtime(realpath(__DIR__ . "/../../assets/js/bootstrap-multiselect.js")),
+ 'assets/js/leaflet/L.MaidenheadColouredGridMap.js',
];
$this->load->view('interface_assets/header', $data);
@@ -927,4 +928,20 @@ class Logbookadvanced extends CI_Controller {
print json_encode($result);
}
+ function showMapForIncorrectGrid() {
+ if(!clubaccess_check(9)) return;
+
+ $this->load->model('logbookadvanced_model');
+ $dxcc = $this->input->post('dxcc', true);
+
+ $data['grids'] = $this->logbookadvanced_model->getGridsForDxcc($dxcc);
+ $data['dxcc'] = $dxcc;
+ $data['gridsquare'] = $this->input->post('gridsquare', true);
+ $dxccname = $this->input->post('dxccname', true);
+ $data['title'] = sprintf(__("Map for DXCC %s and gridsquare %s."), $dxccname, $data['gridsquare']);
+
+ header("Content-Type: application/json");
+ print json_encode($data);
+ }
+
}
diff --git a/application/models/Logbookadvanced_model.php b/application/models/Logbookadvanced_model.php
index dc4243479..3b0694e5c 100644
--- a/application/models/Logbookadvanced_model.php
+++ b/application/models/Logbookadvanced_model.php
@@ -2190,4 +2190,15 @@ class Logbookadvanced_model extends CI_Model {
return $allResults;
}
+
+ function getGridsForDxcc($dxcc) {
+ $sql = "select group_concat(distinct gridsquare order by gridsquare separator ', ') grids
+ from vuccgrids
+ where adif = ?";
+
+ $query = $this->db->query($sql, array($dxcc));
+ $row = $query->row();
+
+ return $row->grids;
+ }
}
diff --git a/application/views/logbookadvanced/checkresult.php b/application/views/logbookadvanced/checkresult.php
index 15d31875d..83ea007e5 100644
--- a/application/views/logbookadvanced/checkresult.php
+++ b/application/views/logbookadvanced/checkresult.php
@@ -165,6 +165,7 @@ function check_incorrect_gridsquares($result, $custom_date_format) { ?>
= __("DXCC"); ?> |
= __("Gridsquare"); ?> |
= __("DXCC Gridsquare"); ?> |
+ = __("Map"); ?> |
@@ -193,6 +194,7 @@ function check_incorrect_gridsquares($result, $custom_date_format) { ?>
}
?>
+ ')"> |
@@ -207,6 +209,7 @@ function check_incorrect_gridsquares($result, $custom_date_format) { ?>
|
|
|
+ |
diff --git a/application/views/logbookadvanced/index.php b/application/views/logbookadvanced/index.php
index 589f86426..6e0521ed7 100644
--- a/application/views/logbookadvanced/index.php
+++ b/application/views/logbookadvanced/index.php
@@ -77,7 +77,8 @@
let lang_gen_advanced_logbook_show_more = '= __("Show more"); ?>';
let lang_gen_advanced_logbook_show_less = '= __("Show less"); ?>';
-
+ let lang_gen_advanced_logbook_confirmedLabel = '= __("Gridsquares for"); ?>';
+ let lang_gen_advanced_logbook_workedLabel = '= __("Non DXCC matching gridsquare"); ?>';
let homegrid ='';
= 2) {
+ const lonIdx = d1.indexOf(grid[0]);
+ const latIdx = d1.indexOf(grid[1]);
+ if (lonIdx >= 0 && latIdx >= 0) {
+ lon += lonIdx * 20;
+ lat += latIdx * 10;
+ lonWidth = 20;
+ latHeight = 10;
+ }
+ }
+
+ // Second pair (square)
+ if (grid.length >= 4) {
+ const lonIdx = parseInt(grid[2]);
+ const latIdx = parseInt(grid[3]);
+ if (!isNaN(lonIdx) && !isNaN(latIdx)) {
+ lon += lonIdx * 2;
+ lat += latIdx * 1;
+ lonWidth = 2;
+ latHeight = 1;
+ }
+ }
+
+ // Third pair (subsquare)
+ if (grid.length >= 6) {
+ const lonIdx = d2.indexOf(grid[4]);
+ const latIdx = d2.indexOf(grid[5]);
+ if (lonIdx >= 0 && latIdx >= 0) {
+ lon += lonIdx * (2 / 24);
+ lat += latIdx * (1 / 24);
+ lonWidth = 2 / 24;
+ latHeight = 1 / 24;
+ }
+ }
+
+ return L.latLngBounds([lat, lon], [lat + latHeight, lon + lonWidth]);
+ }
+
+ function showMapForIncorrectGrid(gridsquare, dxcc, dxccname) {
+ $.ajax({
+ url: base_url + 'index.php/logbookadvanced/showMapForIncorrectGrid',
+ type: 'post',
+ data: {
+ gridsquare: gridsquare,
+ dxcc: dxcc,
+ dxccname: dxccname
+ },
+ success: function (data) {
+ // Add metadata to data object
+ data.gridsquareDisplay = gridsquare;
+ data.dxccnameDisplay = dxccname;
+
+ BootstrapDialog.show({
+ title: data.title,
+ size: BootstrapDialog.SIZE_WIDE,
+ cssClass: 'mapdialog',
+ nl2br: false,
+ message: '',
+ onshown: function(dialog) {
+ drawMap(data);
+ },
+ buttons: [{
+ label: lang_admin_close,
+ action: function (dialogItself) {
+ dialogItself.close();
+ }
+ }]
+ });
+ }
+ });
+ }
+
+ function drawMap(data) {
+ if (typeof(user_map_custom.qsoconfirm) !== 'undefined') {
+ confirmedColor = user_map_custom.qsoconfirm.color;
+ }
+ if (typeof(user_map_custom.qso) !== 'undefined') {
+ workedColor = user_map_custom.qso.color;
+ }
+ let container = L.DomUtil.get('mapgridcontainer');
+
+ if(container != null){
+ container._leaflet_id = null;
+ container.remove();
+ $(".mapgridcontent").html('');
+ }
+
+ // Initialize global arrays for colored maidenhead overlay
+ if (typeof grid_two === 'undefined') grid_two = [];
+ if (typeof grid_four === 'undefined') grid_four = [];
+ if (typeof grid_six === 'undefined') grid_six = [];
+ if (typeof grid_two_confirmed === 'undefined') grid_two_confirmed = [];
+ if (typeof grid_four_confirmed === 'undefined') grid_four_confirmed = [];
+ if (typeof grid_six_confirmed === 'undefined') grid_six_confirmed = [];
+
+ // Clear arrays
+ grid_two.length = 0;
+ grid_four.length = 0;
+ grid_six.length = 0;
+ grid_two_confirmed.length = 0;
+ grid_four_confirmed.length = 0;
+ grid_six_confirmed.length = 0;
+ grids = data.grids;
+
+ // Process data.grids - mark in green (confirmed)
+ if (data.grids) {
+ // data.grids can be a comma-separated string or an array
+ let gridsArray = Array.isArray(data.grids) ? data.grids : data.grids.split(',').map(g => g.trim());
+ gridsArray.forEach(function(grid) {
+ let gridUpper = grid.toUpperCase();
+ if (gridUpper.length === 2) {
+ grid_two_confirmed.push(gridUpper);
+ grid_two.push(gridUpper); // Also add to worked so it shows up
+ } else if (gridUpper.length === 4) {
+ grid_four_confirmed.push(gridUpper);
+ grid_four.push(gridUpper); // Also add to worked so it shows up
+ } else if (gridUpper.length === 6) {
+ grid_six_confirmed.push(gridUpper);
+ grid_six.push(gridUpper); // Also add to worked so it shows up
+ }
+ });
+ }
+
+ // Process data.gridsquare - mark first 4 letters in red (worked)
+ if (data.gridsquare) {
+ let gridsquareUpper = data.gridsquare.toUpperCase().substring(0, 4);
+ if (gridsquareUpper.length >= 2) {
+ let twoChar = gridsquareUpper.substring(0, 2);
+ if (!grid_two_confirmed.includes(twoChar)) {
+ grid_two.push(twoChar);
+ }
+ }
+ if (gridsquareUpper.length >= 4) {
+ let fourChar = gridsquareUpper.substring(0, 4);
+ if (!grid_four_confirmed.includes(fourChar)) {
+ grid_four.push(fourChar);
+ }
+ }
+ }
+
+ // Collect all grids to calculate bounds for auto-zoom
+ // Include both data.grids (green) and data.gridsquare (red)
+ let allGrids = [];
+ if (data.grids) {
+ let gridsArray = Array.isArray(data.grids) ? data.grids : data.grids.split(',').map(g => g.trim());
+ allGrids = allGrids.concat(gridsArray);
+ }
+ if (data.gridsquare) {
+ allGrids.push(data.gridsquare.substring(0, Math.min(4, data.gridsquare.length)));
+ }
+
+ // Calculate bounds and center for auto-zoom
+ let bounds = null;
+ let centerLat = 0;
+ let centerLng = 0;
+ let minLat = 90;
+ let maxLat = -90;
+ let allLngs = [];
+
+ allGrids.forEach(function(grid) {
+ let gridBounds = maidenheadToBounds(grid);
+ if (gridBounds) {
+ // Track center points and extents for better handling
+ let gridCenter = gridBounds.getCenter();
+ centerLat += gridCenter.lat;
+ allLngs.push(gridCenter.lng);
+
+ if (gridBounds.getSouth() < minLat) minLat = gridBounds.getSouth();
+ if (gridBounds.getNorth() > maxLat) maxLat = gridBounds.getNorth();
+
+ if (bounds) {
+ bounds.extend(gridBounds);
+ } else {
+ bounds = gridBounds;
+ }
+ }
+ });
+
+ // Calculate average center
+ if (allLngs.length > 0) {
+ centerLat = centerLat / allGrids.length;
+
+ // Check if longitudes span more than 180° (crossing antimeridian or covering large area)
+ let minLng = Math.min(...allLngs);
+ let maxLng = Math.max(...allLngs);
+ let lngSpan = maxLng - minLng;
+
+ if (lngSpan > 300) {
+ // Spans nearly the entire globe (like Asiatic Russia from -180 to 180)
+ // Use a predefined sensible center for such cases
+ centerLng = 120; // Center of Asiatic Russia/mainland Russia
+ } else if (lngSpan > 180) {
+ // When spanning >180°, we should go the "other way around" the globe
+ // Add 360° to any negative longitudes, then average, then normalize back
+ let wrappedLngs = allLngs.map(lng => lng < 0 ? lng + 360 : lng);
+ let avgWrapped = wrappedLngs.reduce((a, b) => a + b, 0) / wrappedLngs.length;
+
+ // Normalize to -180 to 180 range
+ if (avgWrapped > 180) avgWrapped -= 360;
+ centerLng = avgWrapped;
+ } else {
+ // Normal case - simple average
+ centerLng = allLngs.reduce((a, b) => a + b, 0) / allLngs.length;
+ }
+ }
+
+ // Make map global for L.MaidenheadColouredGridMap.js
+ window.map = new L.Map('mapgridcontainer', {
+ fullscreenControl: true,
+ fullscreenControlOptions: {
+ position: 'topleft'
+ },
+ });
+
+ let maidenhead = L.maidenhead().addTo(window.map);
+
+ let osmUrl = option_map_tile_server;
+ let osmAttrib= option_map_tile_server_copyright;
+ let osm = new L.TileLayer(osmUrl, {minZoom: 1, maxZoom: 12, attribution: osmAttrib});
+
+ let redIcon = L.icon({
+ iconUrl: icon_dot_url,
+ iconSize: [10, 10], // size of the icon
+ });
+
+ window.map.addLayer(osm);
+
+ // Add legend
+ let legend = L.control({position: 'topright'});
+ legend.onAdd = function (map) {
+ let div = L.DomUtil.create('div', 'info legend');
+ div.style.backgroundColor = 'white';
+ div.style.padding = '10px';
+ div.style.borderRadius = '5px';
+ div.style.boxShadow = '0 0 10px rgba(0,0,0,0.2)';
+
+ div.innerHTML =
+ '' +
+ '
' +
+ '
' + lang_gen_advanced_logbook_confirmedLabel + ' ' + data.dxccnameDisplay + '' +
+ '
' +
+ '' +
+ '
' +
+ '
' + lang_gen_advanced_logbook_workedLabel + ' ' + data.gridsquareDisplay + '' +
+ '
';
+ return div;
+ };
+ legend.addTo(window.map);
+
+ // Zoom to fit all grids with padding
+ if (bounds) {
+ const latSpan = maxLat - minLat;
+ const lngSpan = Math.max(...allLngs) - Math.min(...allLngs);
+
+ // For extremely large spans (near 360° like Asiatic Russia), use manual center
+ // For moderate spans (100-200° like Japan+GM05), use fitBounds with lower maxZoom
+ // For smaller spans, use fitBounds normally
+
+ if (lngSpan > 300) {
+ // Spans nearly the entire globe - use calculated center with fixed zoom
+ let zoom = 3; // Increased from 2 to 3 for better detail
+ window.map.setView([centerLat, centerLng], zoom);
+ } else if (lngSpan > 100) {
+ // Large span (like Japan to western hemisphere) - use fitBounds but limit zoom
+ window.map.fitBounds(bounds, { padding: [30, 30], maxZoom: 3 });
+ } else {
+ // Normal case - use fitBounds
+ let maxZoom = 10;
+ if (lngSpan < 50) maxZoom = 7;
+ if (lngSpan < 20) maxZoom = 10;
+ window.map.fitBounds(bounds, { padding: [50, 50], maxZoom: maxZoom });
+ }
+ } else {
+ window.map.setView([30, 0], 1.5);
+ }
+ }