Appearance
UC-029.2: Anti-Doxing Crop Tool & Route Coordinate Memory Optimization โ
๐ 1. Context & Problem Statement โ
When users plan routes or extract rides starting and ending at their homes or offices, they risk exposing sensitive, private physical locations (doxing) when sharing GPX files or routes. To mitigate this privacy risk, users need a simple, reliable way to trim/crop the starting and ending segments of their routes. Trimming must preserve the integrity of the route: the remaining path's distance, elevation, and geometry must be updated correctly, and the cropped ends must be neat rather than jagged straight cuts.
Additionally, standard high-precision routes built in the web application can contain a dense cluster of coordinates (e.g., thousands of coordinates for a 100km route). While this renders smoothly in web browsers, it often leads to "Out of Memory" issues and extreme performance lag on low-power, dedicated hardware GPS computers (e.g., Garmin, Wahoo, smartwatches). To optimize memory footprint and device performance, the system needs:
- Automatic coordinate thinning using the Ramer-Douglas-Peucker (RDP) decimation algorithm with a configurable
toleranceparameter (0.0001 to 0.0005) during GPX exports and route saving. - A strict "Segment Protection" mechanism for manual, off-grid route segments. While automatic road-routed segments can be heavily simplified, manual trail and off-grid segments must retain high density (approx. 20-meter dense chunks) to ensure navigation safety on trails and open terrains.
๐ 2. Database & Data Integrity โ
The existing Drizzle schema for routes (and tracks) is fully compatible. Trimming operations update existing routes in a single database transaction:
routes.polylineโ updated with the new, cropped, and optimized encoded polyline string.routes.distanceโ updated with the newly calculated remaining distance (converted from meters to kilometers, stored in double-precision floating point).
All updates are executed inside a database transaction to ensure that the updated polyline and recalculated distance are saved atomically.
โ๏ธ 3. Backend Logic & API (apps/api) โ
Endpoint: PATCH /api/routes/:id/crop โ
This endpoint accepts trimming parameters in meters:
- Headers:
Authorization: Bearer <token> - Payload:typescript
{ startTrimMeters?: number; // Distance in meters to crop from start endTrimMeters?: number; // Distance in meters to crop from end } - Response:
{ success: true, route: Route }
Haversine-Walk Geometry Trimming Algorithm โ
Trimming cannot simply drop raw coordinates, as GPS points may be hundreds of meters apart, leaving large gaps. Instead, it must compute a neat, interpolated boundary point at the exact trim distance.
Coordinate Traversal (Haversine Walk): โ
- Decode Polyline: Retrieve the
polylinefrom the database and decode it into an array of coordinates[lng, lat]. - Calculate Total Distance: Compute cumulative segment distances using the Haversine formula: $$d = 2 R \arcsin\left(\sqrt{\sin^2\left(\frac{\Delta \text{lat}}{2}\right) + \cos(\text{lat}_1)\cos(\text{lat}_2)\sin^2\left(\frac{\Delta \text{lng}}{2}\right)}\right)$$ Where $R = 6371000$ meters.
- Start Trimming ($startTrimMeters > 0$):
- Traverse coordinates from index
0. Let the current segment be from point $P_i$ to $P_{i+1}$ with length $d_i$. - If the accumulated distance so far plus $d_i < startTrimMeters$, discard $P_i$ and continue.
- If accumulated distance plus $d_i \ge startTrimMeters$, compute the remaining fractional distance: $$f = \frac{startTrimMeters - \text{accumulated}}{d_i}$$
- Interpolate a new coordinate $P_{new}$ at fraction $f$ along the segment $[P_i, P_{i+1}]$ using linear interpolation of longitude and latitude (sufficiently accurate for small segment distances): $$\text{lng}_{new} = \text{lng}i + f \times (\text{lng} - \text{lng}i)$$ $$\text{lat} = \text{lat}i + f \times (\text{lat} - \text{lat}_i)$$
- Discard the entire prefix up to $P_i$ and insert $P_{new}$ as the new starting coordinate at index
0.
- Traverse coordinates from index
- End Trimming ($endTrimMeters > 0$):
- Repeat the cumulative traversal from the tail backwards, or perform the walk from the new start up to the total length minus
endTrimMeters. - Interpolate the new ending coordinate $P'_{new}$ using fraction $f'$ on the boundary segment.
- Discard all coordinates trailing $P'{new}$ and append $P'$ as the final coordinate.
- Repeat the cumulative traversal from the tail backwards, or perform the walk from the new start up to the total length minus
- Re-encode Polyline & Update:
- Calculate the exact length of the remaining coordinates in meters using the Haversine formula.
- Convert the length to kilometers (kilometer-precision distance).
- Re-encode the remaining coordinates to a polyline string.
- Update
routes.polylineandroutes.distancein a transaction.
๐จ 4. User Interface & Interactive Crop Modal (apps/web) โ
Trim & Crop View โ
Create a dedicated component CropRouteModal.tsx launched from the route details screen.
- Map Preview Rendering:
- The original path is displayed on a MapLibre GL instance.
- Remaining Route Path: Rendered as a bold, solid Orange/Peach line (e.g.,
#FF7A00or#FF8E53) representing the clean, post-cropped track. - Cropped Segments: Rendered as semi-transparent dashed gray lines (e.g., color:
#9CA3AF, opacity:0.5, dasharray:[2, 4]) extending from the new start/end to the original start/end.
- Dual-Slider Range Inputs (Sliding Trim Bars):
- Introduce two interactive range sliders at the bottom of the modal:
- Start Trim Slider: Ranges from
0to $30%$ of the route's total distance (in meters). - End Trim Slider: Ranges from
0to $30%$ of the route's total distance (in meters).
- Start Trim Slider: Ranges from
- Sliders must update the map visualization in real-time, instantly adjusting the Peach/Orange remaining path and dashed gray cropped boundaries as the user drags.
- Introduce two interactive range sliders at the bottom of the modal:
- Execution & Toast Notification:
- Clicking
[ โ๏ธ Confirm Crop ]sends thePATCH /api/routes/:id/croprequest, displaying a loading spinner on the button. - On success, triggers a sonner toast:
โ Route cropped successfully! Updated distance: {newDistanceKm} kmand closes the modal, updating the main map view.
- Clicking
โ๏ธ 5. Memory Optimization & GPX Segment Protection โ
Configurable RDP Decimation (Simplify Engine) โ
To optimize GPX file footprint and device memory footprint, an RDP decimation engine is introduced inside packages/shared/src/geo/rdp.ts (or integrated within gpx-utils.ts):
- Tolerance Parameter: Expose a customizable tolerance setting of
0.0001(high precision, low compression) to0.0005(moderate precision, high compression) in coordinate degree space. - Simplification Rule: Run the Ramer-Douglas-Peucker algorithm only on automatic road-routed segments (
routingMode === 'auto'), where straight lines can replace multiple dense points along straight paths without losing detail on curves.
Strict "Segment Protection" (Manual Off-Grid Tracks) โ
When users plan custom routes over rough terrain, forest trails, or off-grid areas (routingMode === 'manual'), they manually lay coordinates to trace the exact track. Standard RDP decimation would wipe out these critical path shapes.
- Protection Rule:
- The coordinate sequence is partitioned into chunks by their
routingMode. - Manual segments are completely bypassed by the RDP simplifier.
- Off-grid manual tracks are preserved as dense 20-meter coordinate chunks. Any manual segment with consecutive points wider than 20m is automatically sub-divided to maintain high-density telemetry.
- The optimized GPX export file merges the simplified automatic segments and protected manual segments seamlessly back together, reducing output file sizes by $80% - 90%$ while guaranteeing absolute off-road navigation safety.
- The coordinate sequence is partitioned into chunks by their
๐งช 6. Testing Requirements โ
Backend Unit & Integration Tests (apps/api) โ
- Haversine Walk Unit Tests: Verify geometry trimming with precise mock coordinates, checking that the new start/end points are precisely interpolated along the segments.
- API Endpoint Test: Mock
PATCH /api/routes/:id/cropcalls, validating input range constraints (e.g. rejecting crops that would leave less than 100 meters or negative values) and ensuring correct database updates in a transaction. - RDP Decimation Tests: Test
simplifyRouteutility with various tolerances (0.0001 to 0.0005) and assert file weight reduction. - Segment Protection Tests: Verify that a hybrid route containing both auto and manual segments preserves the manual segments fully intact while thining the auto segments.
Frontend UI Tests (apps/web) โ
- Interactive Crop Slider Test: Assert that shifting the trim sliders updates the component state correctly and renders updated map coordinates.
- Sonner Toast and Loading States: Verify that saving shows a loader and triggers a successful Sonner notification upon completion.