Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
## 2024-04-15 - [Avoid O(N log N) Sorting on Massive Geographical Collections]
**Learning:** In spatial queries like `findNearbyStations` where we scan `railwayData` containing thousands of stations to find the top K nearest points, allocating all elements to an array and running `Array.prototype.sort()` results in massive temporary object allocation and $O(N \log N)$ execution time (taking ~8.5ms in benchmarks).
**Action:** Replace full array sorts with a bounded Top-K array using a simple $O(K)$ insertion sort during the $O(N)$ iteration phase. This brings the time complexity effectively down to $O(N)$, speeding up operations by ~36x (taking ~0.24ms). Remember to apply a final sort if total elements found are less than $K$.

## 2026-05-03 - [Avoid Expensive Visual Path Generation for Aggregates]
**Learning:** In operations that calculate aggregate statistics over massive lists of data (e.g. total distance across all user trips), calculating visual or rendering-related properties (like SVG paths, rotations, or bounding boxes) for every element causes severe linear slowdowns. Generating data that is ultimately discarded puts pressure on garbage collection and blocks the thread unnecessarily.
**Action:** Always decouple statistical aggregations (like distance) from presentation-layer computations (like SVG generation), or pass a flag to bypass the visual logic entirely when aggregating statistics.
10 changes: 5 additions & 5 deletions src/core/tripCalculator.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export const sliceGeoJsonPath = (feature, startLat, startLng, endLat, endLng) =>
};

// --- Shared Helper: Calculate Visualization Data ---
export const getRouteVisualData = (segments, segmentGeometries, railwayData, geoData) => {
export const getRouteVisualData = (segments, segmentGeometries, railwayData, geoData, skipVisuals = false) => {
let totalDist = 0;
const allCoords = [];

Expand Down Expand Up @@ -215,11 +215,11 @@ export const getRouteVisualData = (segments, segmentGeometries, railwayData, geo
if (geom && geom.coords) {
if (geom.isMulti) {
geom.coords.forEach(c => {
allCoords.push({ coords: c, color: geom.color || '#94a3b8' });
if (!skipVisuals) allCoords.push({ coords: c, color: geom.color || '#94a3b8' });
if(turf) totalDist += turf.length(turf.lineString(c.map(p => [p[1], p[0]])));
});
} else {
allCoords.push({ coords: geom.coords, color: geom.color || '#94a3b8' });
if (!skipVisuals) allCoords.push({ coords: geom.coords, color: geom.color || '#94a3b8' });
if(turf) totalDist += turf.length(turf.lineString(geom.coords.map(p => [p[1], p[0]])));
}
} else {
Expand All @@ -233,7 +233,7 @@ export const getRouteVisualData = (segments, segmentGeometries, railwayData, geo
}
});

if (allCoords.length === 0) return { totalDist, visualPaths: [] };
if (skipVisuals || allCoords.length === 0) return { totalDist, visualPaths: [] };

// PCA & Projection Logic
let sumLat = 0, sumLng = 0, count = 0;
Expand Down Expand Up @@ -328,7 +328,7 @@ export const calculateLatestStats = (trips, segmentGeometries, railwayData, geoD
const uniqueLines = new Set(allSegments.map(s => s.lineKey)).size;

// Calc total distance using helper (aggregating cached or on-the-fly)
const { totalDist: grandTotalDist } = getRouteVisualData(allSegments, segmentGeometries, railwayData, geoData);
const { totalDist: grandTotalDist } = getRouteVisualData(allSegments, segmentGeometries, railwayData, geoData, true);

// 2. Latest 5
const latest = trips.slice(0, 5).map(t => {
Expand Down