From 69da222008251e752a1a286c9073d16840c6b27d Mon Sep 17 00:00:00 2001 From: Alex Vanderpot Date: Fri, 19 Jun 2026 21:29:31 -0400 Subject: [PATCH] Keep neighbor lines when either endpoint is in view Both the server bbox filter (getAllNodeNeighbors) and the client filter (AllNeighborLines) required BOTH endpoints inside the viewport, so any line with one end off-screen vanished when zooming in. Switch both to "either endpoint in view": the server returns an edge if source OR target falls in the bbox, and the client keeps it if either endpoint is a visible node. Each edge carries both endpoints' coordinates, so the off-screen end still draws. Co-Authored-By: Claude Opus 4.8 (1M context) --- meshexplorer/src/components/MapView.tsx | 8 ++++---- meshexplorer/src/lib/clickhouse/actions.ts | 22 ++++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/meshexplorer/src/components/MapView.tsx b/meshexplorer/src/components/MapView.tsx index b9cfa56..93acb54 100644 --- a/meshexplorer/src/components/MapView.tsx +++ b/meshexplorer/src/components/MapView.tsx @@ -431,12 +431,12 @@ function AllNeighborLines({ // Create a set of visible node IDs for quick lookup const visibleNodeIds = new Set(nodes.map(node => node.node_id)); - // Filter connections to only show lines between nodes that are visible on the map - // and meet the minimum packet count threshold. Confidence filtering happens server-side + // Keep a line if EITHER endpoint is visible on the map, so connections that leave the viewport + // (one node on-screen, the other off-screen) stay drawn. Each edge carries both endpoints' + // coordinates, so the off-screen end renders fine. Confidence filtering happens server-side // (the confidence selector drives a refetch), so no client-side confidence filter here. const visibleConnections = connections.filter(connection => - visibleNodeIds.has(connection.source_node) && - visibleNodeIds.has(connection.target_node) && + (visibleNodeIds.has(connection.source_node) || visibleNodeIds.has(connection.target_node)) && connection.packet_count >= minPacketCount ); diff --git a/meshexplorer/src/lib/clickhouse/actions.ts b/meshexplorer/src/lib/clickhouse/actions.ts index 979b4e1..9f71660 100644 --- a/meshexplorer/src/lib/clickhouse/actions.ts +++ b/meshexplorer/src/lib/clickhouse/actions.ts @@ -332,21 +332,19 @@ export async function getAllNodeNeighbors(lastSeen: string | null = null, minLat visibleNodeSqlClause("target_name"), ]; - // Bounding box: both endpoints must be within view (matches the old visible_nodes behavior) - if (minLat !== null && minLat !== undefined && minLat !== "") { - whereConditions.push("source_latitude >= {minLat:Float64} AND target_latitude >= {minLat:Float64}"); + // Bounding box: keep an edge if EITHER endpoint is within view, so connections that leave the + // viewport (one node on-screen, the other off-screen) stay visible. Both endpoints' coordinates + // are denormalized on the edge, so the off-screen end still draws. + const bbox = [minLat, maxLat, minLng, maxLng]; + const hasBbox = bbox.every(v => v !== null && v !== undefined && v !== ""); + if (hasBbox) { + whereConditions.push(`( + (source_latitude BETWEEN {minLat:Float64} AND {maxLat:Float64} AND source_longitude BETWEEN {minLng:Float64} AND {maxLng:Float64}) + OR (target_latitude BETWEEN {minLat:Float64} AND {maxLat:Float64} AND target_longitude BETWEEN {minLng:Float64} AND {maxLng:Float64}) + )`); params.minLat = Number(minLat); - } - if (maxLat !== null && maxLat !== undefined && maxLat !== "") { - whereConditions.push("source_latitude <= {maxLat:Float64} AND target_latitude <= {maxLat:Float64}"); params.maxLat = Number(maxLat); - } - if (minLng !== null && minLng !== undefined && minLng !== "") { - whereConditions.push("source_longitude >= {minLng:Float64} AND target_longitude >= {minLng:Float64}"); params.minLng = Number(minLng); - } - if (maxLng !== null && maxLng !== undefined && maxLng !== "") { - whereConditions.push("source_longitude <= {maxLng:Float64} AND target_longitude <= {maxLng:Float64}"); params.maxLng = Number(maxLng); } if (lastSeen !== null && lastSeen !== undefined && lastSeen !== "") {