mirror of
https://github.com/smittix/intercept.git
synced 2026-07-23 16:38:11 -07:00
Fix heatmap for towers with CID=0 or no geocoded coordinates
The monitored tower may have CID=0 (partially decoded cell) which OpenCellID can't geocode, leaving it without coordinates. The heatmap now falls back through: monitored tower by ARFCN, any geocoded tower, then observer location. Also tracks the monitored ARFCN so the fallback can find the right tower even when CID matching fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1818,6 +1818,7 @@
|
|||||||
let heatmapLayer = null;
|
let heatmapLayer = null;
|
||||||
let heatmapVisible = false;
|
let heatmapVisible = false;
|
||||||
let heatmapLastRefresh = 0;
|
let heatmapLastRefresh = 0;
|
||||||
|
let monitoredArfcn = null;
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
let stats = {
|
let stats = {
|
||||||
@@ -2168,6 +2169,7 @@
|
|||||||
updateScanStatus('Scan #' + data.scan + ' complete (' + data.towers_found + ' towers, ' + data.duration + 's)');
|
updateScanStatus('Scan #' + data.scan + ' complete (' + data.towers_found + ' towers, ' + data.duration + 's)');
|
||||||
document.getElementById('scanProgressBar').style.width = '100%';
|
document.getElementById('scanProgressBar').style.width = '100%';
|
||||||
} else if (data.type === 'auto_monitor_started') {
|
} else if (data.type === 'auto_monitor_started') {
|
||||||
|
monitoredArfcn = data.arfcn;
|
||||||
showMonitorStatus(data.arfcn);
|
showMonitorStatus(data.arfcn);
|
||||||
console.log('[GSM SPY] Auto-monitor started on ARFCN', data.arfcn);
|
console.log('[GSM SPY] Auto-monitor started on ARFCN', data.arfcn);
|
||||||
} else if (data.type === 'monitor_heartbeat') {
|
} else if (data.type === 'monitor_heartbeat') {
|
||||||
@@ -3172,6 +3174,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findTowerCoords(cid, lac) {
|
||||||
|
// Try exact CID+LAC match first
|
||||||
|
for (const key in towers) {
|
||||||
|
const t = towers[key];
|
||||||
|
if (Number(t.cid) === cid && Number(t.lac) === lac &&
|
||||||
|
t.lat && t.lon && !isNaN(parseFloat(t.lat)) && !isNaN(parseFloat(t.lon))) {
|
||||||
|
return [parseFloat(t.lat), parseFloat(t.lon)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findFallbackCoords() {
|
||||||
|
// 1) Try the monitored tower (by ARFCN)
|
||||||
|
if (monitoredArfcn !== null) {
|
||||||
|
for (const key in towers) {
|
||||||
|
const t = towers[key];
|
||||||
|
if (Number(t.arfcn) === Number(monitoredArfcn) &&
|
||||||
|
t.lat && t.lon && !isNaN(parseFloat(t.lat)) && !isNaN(parseFloat(t.lon))) {
|
||||||
|
console.log(`[GSM SPY] Heatmap: using monitored tower ARFCN ${monitoredArfcn} coords as fallback`);
|
||||||
|
return [parseFloat(t.lat), parseFloat(t.lon)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2) Try any geocoded tower
|
||||||
|
for (const key in towers) {
|
||||||
|
const t = towers[key];
|
||||||
|
if (t.lat && t.lon && !isNaN(parseFloat(t.lat)) && !isNaN(parseFloat(t.lon))) {
|
||||||
|
console.log(`[GSM SPY] Heatmap: using tower CID ${t.cid} coords as fallback`);
|
||||||
|
return [parseFloat(t.lat), parseFloat(t.lon)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3) Fall back to observer location
|
||||||
|
const obsLat = parseFloat(document.getElementById('obsLat').value);
|
||||||
|
const obsLon = parseFloat(document.getElementById('obsLon').value);
|
||||||
|
if (!isNaN(obsLat) && !isNaN(obsLon)) {
|
||||||
|
console.log('[GSM SPY] Heatmap: using observer location as fallback');
|
||||||
|
return [obsLat, obsLon];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async function updateHeatmap() {
|
async function updateHeatmap() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/gsm_spy/crowd_density?hours=1');
|
const response = await fetch('/gsm_spy/crowd_density?hours=1');
|
||||||
@@ -3188,39 +3232,28 @@
|
|||||||
|
|
||||||
const points = [];
|
const points = [];
|
||||||
let maxIntensity = 1;
|
let maxIntensity = 1;
|
||||||
let matched = 0;
|
const fallbackCoords = findFallbackCoords();
|
||||||
let unmatched = 0;
|
|
||||||
|
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
const itemCid = Number(item.cid);
|
const itemCid = Number(item.cid);
|
||||||
const itemLac = Number(item.lac);
|
const itemLac = Number(item.lac);
|
||||||
let found = false;
|
const intensity = item.unique_devices || 1;
|
||||||
|
if (intensity > maxIntensity) maxIntensity = intensity;
|
||||||
|
|
||||||
for (const key in towers) {
|
// Try exact CID+LAC tower match
|
||||||
const t = towers[key];
|
const coords = findTowerCoords(itemCid, itemLac);
|
||||||
if (Number(t.cid) === itemCid && Number(t.lac) === itemLac) {
|
if (coords) {
|
||||||
if (t.lat && t.lon && !isNaN(parseFloat(t.lat)) && !isNaN(parseFloat(t.lon))) {
|
points.push([coords[0], coords[1], intensity]);
|
||||||
const intensity = item.unique_devices || 1;
|
} else if (fallbackCoords) {
|
||||||
if (intensity > maxIntensity) maxIntensity = intensity;
|
// Tower not geocoded or CID=0 — use fallback location
|
||||||
points.push([parseFloat(t.lat), parseFloat(t.lon), intensity]);
|
points.push([fallbackCoords[0], fallbackCoords[1], intensity]);
|
||||||
found = true;
|
|
||||||
} else {
|
|
||||||
console.log(`[GSM SPY] Heatmap: tower CID ${itemCid} LAC ${itemLac} has no coordinates yet`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (found) matched++;
|
|
||||||
else unmatched++;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`[GSM SPY] Heatmap: ${matched} matched, ${unmatched} unmatched, ${points.length} points with coords`);
|
console.log(`[GSM SPY] Heatmap: ${points.length} points from ${data.length} density records`);
|
||||||
|
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
if (!heatmapVisible) {
|
console.warn('[GSM SPY] Heatmap: no plottable points (no tower coords and no fallback)');
|
||||||
console.warn('[GSM SPY] Heatmap: density data exists but no towers have coordinates. Wait for geocoding.');
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user