mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-25 20:45:17 +00:00
fix: map labels show short hash ID (e.g. 5B, BEEF), better deconfliction with spiral offsets
This commit is contained in:
+1
-1
@@ -83,7 +83,7 @@
|
||||
<script src="app.js?v=1774052279"></script>
|
||||
<script src="home.js?v=1774042199"></script>
|
||||
<script src="packets.js?v=1774052279"></script>
|
||||
<script src="map.js?v=1774052755" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="map.js?v=1774053025" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="channels.js?v=1774050030" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="nodes.js?v=1774050030" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="traces.js?v=1774048777" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
|
||||
+35
-24
@@ -62,9 +62,12 @@
|
||||
|
||||
function makeRepeaterLabelIcon(node) {
|
||||
var s = ROLE_STYLE['repeater'] || ROLE_STYLE.companion;
|
||||
var label = node.hash_size ? node.hash_size + 'B' : '?';
|
||||
var bgColor = node.hash_size ? s.color : '#999';
|
||||
var html = '<div style="background:' + bgColor + ';color:#fff;font-weight:bold;font-size:12px;padding:3px 7px;border-radius:4px;border:2px solid #fff;box-shadow:0 1px 3px rgba(0,0,0,0.4);text-align:center;line-height:1;">' + label + '</div>';
|
||||
var hs = node.hash_size || 1;
|
||||
// Show the short mesh hash ID (first N bytes of pubkey, uppercased)
|
||||
var shortHash = node.public_key ? node.public_key.slice(0, hs * 2).toUpperCase() : '??';
|
||||
var bgColor = node.hash_size ? s.color : '#888';
|
||||
var html = '<div style="background:' + bgColor + ';color:#fff;font-weight:bold;font-size:11px;padding:2px 5px;border-radius:3px;border:2px solid #fff;box-shadow:0 1px 3px rgba(0,0,0,0.4);text-align:center;line-height:1.2;white-space:nowrap;">' +
|
||||
shortHash + '</div>';
|
||||
return L.divIcon({
|
||||
html: html,
|
||||
className: 'meshcore-marker meshcore-label-marker',
|
||||
@@ -374,31 +377,39 @@
|
||||
var _renderingMarkers = false;
|
||||
var _lastDeconflictZoom = null;
|
||||
|
||||
function deconflictLabels(labelMarkers, map) {
|
||||
function deconflictLabels(labelMarkers, mapRef) {
|
||||
const placed = [];
|
||||
const LABEL_W = 32;
|
||||
const LABEL_H = 22;
|
||||
const PAD = 4;
|
||||
const LABEL_W = 38;
|
||||
const LABEL_H = 24;
|
||||
const PAD = 6;
|
||||
|
||||
const overlaps = (b) => placed.some(p =>
|
||||
b.x < p.x + p.w + PAD && b.x + b.w + PAD > p.x &&
|
||||
b.y < p.y + p.h + PAD && b.y + b.h + PAD > p.y
|
||||
);
|
||||
const overlaps = function(b) {
|
||||
return placed.some(function(p) {
|
||||
return b.x < p.x + p.w + PAD && b.x + b.w + PAD > p.x &&
|
||||
b.y < p.y + p.h + PAD && b.y + b.h + PAD > p.y;
|
||||
});
|
||||
};
|
||||
|
||||
for (const m of labelMarkers) {
|
||||
const pt = map.latLngToLayerPoint(m.latLng);
|
||||
let bestPt = pt;
|
||||
let box = { x: pt.x - LABEL_W / 2, y: pt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
|
||||
// Generate spiral offsets — 5 rings, 8 directions each, up to 120px out
|
||||
var offsets = [];
|
||||
for (var ring = 1; ring <= 6; ring++) {
|
||||
var dist = ring * 28;
|
||||
for (var angle = 0; angle < 360; angle += 45) {
|
||||
var rad = angle * Math.PI / 180;
|
||||
offsets.push([Math.round(Math.cos(rad) * dist), Math.round(Math.sin(rad) * dist)]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < labelMarkers.length; i++) {
|
||||
var m = labelMarkers[i];
|
||||
var pt = mapRef.latLngToLayerPoint(m.latLng);
|
||||
var bestPt = pt;
|
||||
var box = { x: pt.x - LABEL_W / 2, y: pt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
|
||||
|
||||
if (overlaps(box)) {
|
||||
const offsets = [
|
||||
[0, -25], [0, 25], [-30, 0], [30, 0],
|
||||
[-25, -20], [25, -20], [-25, 20], [25, 20],
|
||||
[0, -45], [0, 45], [-50, 0], [50, 0]
|
||||
];
|
||||
for (const [dx, dy] of offsets) {
|
||||
const tryPt = L.point(pt.x + dx, pt.y + dy);
|
||||
const tryBox = { x: tryPt.x - LABEL_W / 2, y: tryPt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
|
||||
for (var j = 0; j < offsets.length; j++) {
|
||||
var tryPt = L.point(pt.x + offsets[j][0], pt.y + offsets[j][1]);
|
||||
var tryBox = { x: tryPt.x - LABEL_W / 2, y: tryPt.y - LABEL_H / 2, w: LABEL_W, h: LABEL_H };
|
||||
if (!overlaps(tryBox)) {
|
||||
bestPt = tryPt;
|
||||
box = tryBox;
|
||||
@@ -408,7 +419,7 @@
|
||||
}
|
||||
|
||||
placed.push(box);
|
||||
m.adjustedLatLng = map.layerPointToLatLng(bestPt);
|
||||
m.adjustedLatLng = mapRef.layerPointToLatLng(bestPt);
|
||||
m.offset = Math.sqrt(Math.pow(bestPt.x - pt.x, 2) + Math.pow(bestPt.y - pt.y, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,20 +973,45 @@ app.get('/api/nodes', (req, res) => {
|
||||
counts[r + 's'] = db.db.prepare(`SELECT COUNT(*) as count FROM nodes WHERE role = ?`).get(r).count;
|
||||
}
|
||||
|
||||
// Compute hash_size for each node from latest ADVERT packets
|
||||
// Compute hash_size for each node from ADVERT path byte or path hop lengths
|
||||
const hashSizeMap = new Map();
|
||||
// Pass 1: from ADVERT packets (most authoritative — path byte bits 7-6)
|
||||
for (const p of pktStore.packets) {
|
||||
if (p.payload_type === 4 && p.decoded_json) {
|
||||
if (p.payload_type === 4 && p.raw_hex) {
|
||||
try {
|
||||
const d = JSON.parse(p.decoded_json);
|
||||
const d = JSON.parse(p.decoded_json || '{}');
|
||||
const pk = d.pubKey || d.public_key;
|
||||
if (pk && p.raw_hex && !hashSizeMap.has(pk)) {
|
||||
if (pk && !hashSizeMap.has(pk)) {
|
||||
const pathByte = parseInt(p.raw_hex.slice(2, 4), 16);
|
||||
hashSizeMap.set(pk, ((pathByte >> 6) & 0x3) + 1);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
// Pass 2: for nodes without ADVERTs, derive from path hop lengths in any packet
|
||||
// Path hops are hex strings; their length / 2 = hash_size in bytes
|
||||
for (const p of pktStore.packets) {
|
||||
if (p.path_json) {
|
||||
try {
|
||||
const hops = JSON.parse(p.path_json);
|
||||
if (hops.length > 0) {
|
||||
const hopLen = hops[0].length / 2; // hex chars / 2 = bytes
|
||||
if (hopLen >= 1 && hopLen <= 4) {
|
||||
// The path byte tells us hash_size for ALL nodes in this packet's mesh
|
||||
const pathByte = p.raw_hex ? parseInt(p.raw_hex.slice(2, 4), 16) : -1;
|
||||
const hs = pathByte >= 0 ? ((pathByte >> 6) & 0x3) + 1 : hopLen;
|
||||
// We can't map hops to pubkeys directly (hops are truncated hashes)
|
||||
// but we know the packet's source node uses this hash_size
|
||||
if (p.decoded_json) {
|
||||
const d = JSON.parse(p.decoded_json);
|
||||
const pk = d.pubKey || d.public_key;
|
||||
if (pk && !hashSizeMap.has(pk)) hashSizeMap.set(pk, hs);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
for (const node of nodes) {
|
||||
node.hash_size = hashSizeMap.get(node.public_key) || null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user