diff --git a/meshview/templates/map.html b/meshview/templates/map.html
index 02af414..9132f7d 100644
--- a/meshview/templates/map.html
+++ b/meshview/templates/map.html
@@ -34,17 +34,13 @@
-
+ Show Routers Only
-
- Show Routers Only
-
-
+ // --- Lazy load edges on node click ---
+ var edgeLayer = L.layerGroup().addTo(map); // initially empty
+ var edgesData = null;
-{% endblock %}
\ No newline at end of file
+ function loadEdges(callback) {
+ if (edgesData) {
+ callback(edgesData);
+ } else {
+ fetch('/api/edges')
+ .then(res => res.json())
+ .then(data => {
+ edgesData = data.edges;
+ callback(edgesData);
+ })
+ .catch(err => console.error("Error loading edges:", err));
+ }
+ }
+
+ function onNodeClick(node) {
+ loadEdges(function(edges) {
+ edgeLayer.clearLayers(); // remove previous edges
+
+ edges.forEach(edge => {
+ if (edge.from === node.id || edge.to === node.id) {
+ let from = nodeIndex[edge.from];
+ let to = nodeIndex[edge.to];
+ if (from && to) {
+ let poly = L.polyline([from, to], {
+ color: edge.type === "neighbor" ? "red" : "blue",
+ weight: 2,
+ opacity: 1,
+ dashArray: edge.type === "traceroute" ? "5,5" : null
+ }).addTo(edgeLayer);
+
+ poly.bringToBack();
+ poly.setStyle({opacity: 1}); // highlight edge immediately
+ }
+ }
+ });
+ });
+ }
+
+ // Attach click events to nodes
+ nodes.forEach(node => {
+ if (node.lat !== null && node.long !== null) {
+ let marker = markers[node.channel].find(obj =>
+ obj.marker.getLatLng().lat === node.lat &&
+ obj.marker.getLatLng().lng === node.long
+ ).marker;
+
+ marker.on('click', () => onNodeClick(node));
+ }
+ });
+
+{% endblock %}
diff --git a/meshview/web.py b/meshview/web.py
index 278b314..ad1a264 100644
--- a/meshview/web.py
+++ b/meshview/web.py
@@ -1705,6 +1705,43 @@ async def api_config(request):
except Exception as e:
return web.json_response({"error": str(e)}, status=500)
+@routes.get("/api/edges")
+async def api_edges(request):
+ edges_set = set()
+ edge_type = {}
+ since = datetime.timedelta(hours=48)
+
+ # Fetch traceroutes
+ for tr in await store.get_traceroutes(since):
+ route = decode_payload.decode_payload(PortNum.TRACEROUTE_APP, tr.route)
+ path = [tr.packet.from_node_id] + list(route.route) # Convert to list
+ if tr.done:
+ path.append(tr.packet.to_node_id)
+ else:
+ if path[-1] != tr.gateway_node_id:
+ path.append(tr.gateway_node_id)
+
+ for i in range(len(path) - 1):
+ edge_pair = (path[i], path[i + 1])
+ edges_set.add(edge_pair)
+ edge_type[edge_pair] = "traceroute"
+
+ # Fetch NeighborInfo packets
+ for packet in await store.get_packets(portnum=PortNum.NEIGHBORINFO_APP, after=since):
+ try:
+ _, neighbor_info = decode_payload.decode(packet)
+ for node in neighbor_info.neighbors:
+ edge_pair = (node.node_id, packet.from_node_id)
+ if edge_pair not in edges_set:
+ edges_set.add(edge_pair)
+ edge_type[edge_pair] = "neighbor"
+ except Exception as e:
+ print(f"Error decoding NeighborInfo packet: {e}")
+
+ # Prepare edges with type only
+ edges = [{"from": frm, "to": to, "type": edge_type[(frm, to)]} for frm, to in edges_set]
+
+ return web.json_response({"edges": edges})
# Generic static HTML route