Files
rfcp/docs/devlog/gpu_supp/RFCP-3.10.4-TerrainClick-TxHeight.md
2026-02-07 12:56:25 +02:00

5.4 KiB

RFCP — Iteration 3.10.4: Terrain Profile Click Fix & TX Height

Two bugs remaining from previous iterations.


Bug 1: Terrain Profile click still places ruler point

Problem: Clicking inside the Terrain Profile popup (chart area, close button, fresnel checkbox, anywhere in the popup) triggers the map click handler underneath, which places a ruler point or resets the measurement.

Previous fix was incomplete — stopPropagation was added to some elements but not the entire popup container and its backdrop.

Fix: The Terrain Profile popup needs a FULL click barrier. Every mouse event must be caught:

// The OUTERMOST container of the Terrain Profile popup:
<div
  className="terrain-profile-container"
  onClick={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
  onMouseDown={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
  onMouseUp={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
  onPointerDown={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
  onPointerUp={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
  onDoubleClick={(e) => { e.stopPropagation(); e.nativeEvent.stopImmediatePropagation(); }}
>
  {/* All terrain profile content */}
</div>

IMPORTANT: stopPropagation() alone may not be enough because Leaflet listens to DOM events directly, not React synthetic events. The fix MUST also call e.nativeEvent.stopImmediatePropagation() to prevent Leaflet's native DOM listener from firing.

Alternative approach (more robust): Add the popup OUTSIDE the Leaflet map container in the DOM tree. If the Terrain Profile div is a sibling or parent of the map div (not a child), Leaflet's event delegation won't catch clicks on it at all.

// In the main layout:
<div className="app-layout">
  <div id="map-container">
    {/* Leaflet map renders here */}
  </div>
  
  {/* These are OUTSIDE the map container — Leaflet can't intercept */}
  {showTerrainProfile && (
    <TerrainProfile ... />
  )}
  {showLinkBudget && (
    <LinkBudgetPanel ... />
  )}
</div>

If moving outside the map container is too much refactoring, the stopImmediatePropagation approach should work. But check: is the TerrainProfile component rendered INSIDE a Leaflet pane or overlay? If so, moving it out is the correct fix.

Also apply the same fix to:

  • Link Budget Calculator panel
  • Any other floating panel/popup that sits over the map

Problem: The Link Budget Calculator TRANSMITTER section always shows Height: 2m regardless of the actual site configuration. It should read the height from the selected site's settings.

Root cause: The LinkBudgetPanel component likely reads site.height but the site object might store height in a different field name (e.g., site.antennaHeight, site.towerHeight, site.params.height, or per-sector height).

Fix: Find where site height is stored and pass the correct value:

// In LinkBudgetPanel.tsx, find where TX height is set:
// WRONG (probably current):
const txHeight = site.height || 2;  // Defaults to 2 if field is missing

// Check the actual site data structure. It might be:
const txHeight = site.antennaHeight 
  || site.tower_height 
  || site.params?.height
  || site.sectors?.[0]?.height  // If height is per-sector
  || 30;  // Default should be 30m for a typical cell tower, not 2m

// Or if height is stored in meters in a nested config:
const txHeight = selectedSite?.config?.height || selectedSite?.height || 30;

Steps to debug:

  1. In the browser console (F12), find the selected site object
  2. Check what field contains the height value
  3. Update LinkBudgetPanel to read from the correct field

Display fix:

// In the TRANSMITTER section of the panel:
<div className="param-row">
  <span>Height:</span>
  <span>{txHeight} m</span>
</div>

The height should also be EDITABLE in the link budget calculator (as an input field, not just display), since you might want to test "what if I put the antenna at 40m instead of 30m?" without changing the actual site config.

// Make height an editable field with site value as default:
const [txHeightOverride, setTxHeightOverride] = useState<number | null>(null);
const txHeight = txHeightOverride ?? (site?.height || 30);

<div className="param-row">
  <label>Height:</label>
  <input 
    type="number" 
    value={txHeight}
    onChange={(e) => setTxHeightOverride(parseFloat(e.target.value))}
  /> m
</div>

Testing Checklist

  • Click ANYWHERE inside Terrain Profile popup — NO ruler point placed
  • Click Terrain Profile close button (X) — popup closes, no ruler point
  • Click Fresnel Zone checkbox — toggles, no ruler point
  • Click chart area — no ruler point
  • Drag/scroll inside chart — no map pan/zoom
  • TX Height in Link Budget shows actual site height (not 2m)
  • TX Height is editable for what-if scenarios
  • Changing TX height recalculates link budget

Commit Message

fix(ui): block all click propagation from terrain profile, fix TX height

- Add stopImmediatePropagation on terrain profile container
- Prevent all mouse/pointer events from reaching Leaflet map
- Fix TX height reading from site config (was defaulting to 2m)
- Make TX height editable in link budget calculator