Skip to content

UC-029.1: Route Library & Ride-to-Route Extraction Engine ​

πŸ“– 1. Context & Problem Statement ​

Historical Strava activities contain valuable GPS data (summary_polyline). Users need a way to extract these past rides and save them as permanent, reusable "Routes" in a personal Library for future navigation. Additionally, when extracting multiple times or duplicating routes, the system must prevent naming collisions using a smart string-suffix algorithm.

πŸ—„ 2. Database Schema (Drizzle ORM) ​

Create the routes table:

  • id (uuid, primary key)
  • userId (uuid, foreign key referencing users.id, non-null)
  • sourceActivityId (uuid, foreign key referencing activities.id, nullable)
  • name (varchar, non-null)
  • distance (double precision, non-null)
  • polyline (text, non-null) β€” The encoded GPS path for MapLibre rendering.
  • isEbike (boolean, default false) β€” Auto-tagged if extracted from an EBikeRide.
  • createdAt (timestamp, default now)

βš™οΈ 3. Backend Logic & API (apps/api) ​

Endpoint: POST /api/routes/extract/:activityId

  1. Fetch the target activity. Ensure summary_polyline exists (Return 400 if it was an indoor ride with no GPS).
  2. Smart Naming Algorithm (The "Copy" Logic):
    • Query existing routes owned by the user.
    • If original name is "Morning Ride", check for exact matches.
    • If match found, append suffix -> "Morning Ride (Copy)".
    • If (Copy) exists, extract the number and increment -> "Morning Ride (Copy 2)", then (Copy 3), etc.
  3. Save the new route to the routes table. Auto-set isEbike = true if the source activity was EBikeRide.

Endpoint: GET /api/routes

  • Return the user's saved routes, sorted by newest first.

🎨 4. User Interface (apps/web) ​

  1. Activity History Action: In HistoryLog.tsx, wire up the [ πŸ—ΊοΈ Generate Route ] button to call the /extract API. Show a loading spinner during execution. On success, show a toast: βœ… Route Saved: {Route Name}.
  2. Route Library View: Create a new page/view /library (or a tab in the Garage) that displays a list of the user's saved Routes.
  3. Library Cards: Each card displays the Route Name, Distance, an E-Bike badge (if applicable), and a [ πŸ—ΊοΈ Open on Map ] button (which will route to the MapLibre engine in the next phase).

πŸ§ͺ 5. Testing Requirements ​

  • Backend: Pure unit tests for the Smart Naming Algorithm ensuring it correctly increments (Copy) to (Copy 2) and (Copy 3). Integration test verifying isEbike is parsed correctly.
  • Frontend: Verify the button loading states and toast notifications.## πŸ›  5. Implementation Log

Status: βœ… Completed (May 2026)

  • Database: routes table created via Migration 0020 with cascade deletes.
  • Backend: POST /api/routes/extract implemented with Smart Naming Algorithm (safe transaction-based regex for (Copy) suffixes) and isEbike auto-tagging. Rejected indoor rides missing GPS data.
  • Frontend: Built filterable Library View with Layout Switcher, E-Bike badges, and native polyline-to-GeoJSON decoding that dispatches the geometry directly into the MapLibre interactive map store.
  • Testing: 100% green across 872 tests. Pure math and regex thoroughly covered.