From 9caa0947b69489cc6975248245152074f8e275f6 Mon Sep 17 00:00:00 2001 From: quanticsoul4772 <71096528+quanticsoul4772@users.noreply.github.com> Date: Sun, 1 Nov 2026 07:43:22 -0800 Subject: [PATCH] fix(traffic): keep a TomTom side from pairing with its own pieces A one_side line that doubles back within 5 m of itself, such as a turnaround drawn as one line, matched its own return leg and took a two-way half share with no partner. Flow index pieces now carry their source line, and a radiusFor of 0 leaves a piece out of nearest(), so pairing skips the line's own pieces. Co-Authored-By: Claude Opus 5.5 --- src/data/flowMatch.js | 6 ++++-- src/layers/traffic/roadModes.js | 3 ++- src/layers/traffic/roadModes.test.mjs | 8 ++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/data/flowMatch.js b/src/data/flowMatch.js index c050f6917..cc4fe2280 100644 --- a/src/data/flowMatch.js +++ b/src/data/flowMatch.js @@ -139,6 +139,7 @@ export function createFlowSegmentIndex(flowSegments) { roadType, level, closure, + flow, }; const key = cellKey( Math.floor((seg.ax + seg.bx) / 2 / CELL_SIZE_M), @@ -158,7 +159,8 @@ export function createFlowSegmentIndex(flowSegments) { const radius2 = MATCH_RADIUS_M * MATCH_RADIUS_M; /** * Nearest direction-compatible segment within 35 m of a projected point. - * `radiusFor(segment)` may tighten the radius per segment (never widen it). + * `radiusFor(segment)` may tighten the radius per segment (never widen it); + * 0 leaves the segment out. */ function nearest(px, py, bearing, radiusFor = null) { const cx = Math.floor(px / CELL_SIZE_M); @@ -177,7 +179,7 @@ export function createFlowSegmentIndex(flowSegments) { candidate = true; // within radius, bearing not yet checked if (radiusFor) { const radius = Math.min(MATCH_RADIUS_M, radiusFor(seg)); - if (d2 > radius * radius) continue; + if (!(radius > 0) || d2 > radius * radius) continue; } const diff = bearingDiffDeg(seg.bearing, bearing); if ( diff --git a/src/layers/traffic/roadModes.js b/src/layers/traffic/roadModes.js index e9220b635..a6eecc5b5 100644 --- a/src/layers/traffic/roadModes.js +++ b/src/layers/traffic/roadModes.js @@ -126,7 +126,8 @@ function pairedSampleShare(segment, index) { xs[i - 1] + dx * t, ys[i - 1] + dy * t, reverse, - () => PAIRED_SIDE_RADIUS_M, + // A line that doubles back beside itself is not its own pair. + (piece) => (piece.flow === segment ? 0 : PAIRED_SIDE_RADIUS_M), ).best ) paired++; diff --git a/src/layers/traffic/roadModes.test.mjs b/src/layers/traffic/roadModes.test.mjs index 465d41496..406dc934b 100644 --- a/src/layers/traffic/roadModes.test.mjs +++ b/src/layers/traffic/roadModes.test.mjs @@ -163,6 +163,14 @@ test('a TomTom side paired along only part of its length keeps its share', () => assert.equal(weightOfFlow(partner(-97.7434)), 1); assert.equal(weightOfFlow(partner(-97.7428)), 0.5); }); +test('a TomTom side that doubles back beside itself is not its own pair', () => { + // East along `coordinates`, then back west about 1 m north. + const hairpin = { + ...flow, + coords: [...coordinates, [-97.742, 30.26701], [-97.744, 30.26701]], + }; + assert.equal(flowSegmentsToRoads([hairpin])[0].densityWeight ?? 1, 1); +}); test('bearing and distance reject nearby crossing and separate OSM roads', () => { const parallel = { ...main,