Back to Projects
Software
FastAPIFlutterDockerValhallaPostGIS

Detour

A smart navigation app that scores routes based on driving difficulty, stress levels, and user preferences rather than just fastest time.

Project Overview

Detour is a personal navigation app I built that goes beyond traditional turn-by-turn directions. Instead of just showing the fastest route, Detour analyzes routes for driving difficulty, stress levels, traffic conditions, and alignment with user preferences.

The app learns from your driving history and preferences to recommend routes that match how you actually want to drive.

Role: Solo developer

Status: Unfinished demo

Detour app showing route options in Las Vegas


The Problem

Traditional navigation apps optimize for one thing: time. But that's not always what drivers want:

  • New drivers might want to avoid complex highway merges
  • Some people prefer surface streets even if it takes longer
  • Others want to avoid left turns across traffic
  • Familiar routes feel more comfortable than optimal ones

Detour scores routes on multiple dimensions and lets users configure their preferences.


Architecture

Docker Compose Services:

ServiceRolePort
Detour APIFastAPI backend, route scoring8000
ValhallaOSM routing engine8002
PostGISSpatial database5432

Client:

ComponentTechnology
Mobile AppFlutter with MapLibre GL

Route Scoring System

Each route is scored across multiple dimensions:

ScoreWhat It Measures
StressHighway merges, complex intersections, turn frequency
ExecutabilityDifficulty of individual maneuvers (hard left turns, short merge lanes)
PredictabilityHow consistent the route is (highways vary more with traffic)
Preference AlignmentMatch against user's stated preferences
TrafficReal-time congestion and incidents via HERE API
FamiliarityRoutes the user has taken before get a boost

Routes are ranked by a weighted combination of these scores, not just travel time.


Executability Analysis

The API analyzes every maneuver for difficulty:

# Maneuvers are tagged as easy, moderate, or hard
exec_result = analyze_route(raw_maneuvers)

# Hard maneuvers get warnings
warnings_map = {}
for mi, diff in enumerate(exec_result["difficulty_per_maneuver"]):
    if diff == "hard":
        warnings_map[mi] = exec_result["warnings"][warning_idx]

Examples of hard maneuvers:

  • Left turn across 4+ lanes of traffic
  • Short highway merge lanes
  • Complex interchange weaves
  • Unprotected left turns at busy intersections

User Preferences

Users can configure their routing preferences:

class Preference:
    avoid_highways: bool
    avoid_left_turns: bool
    prefer_familiar_routes: bool
    avoid_heavy_traffic: bool
    avoid_incidents: bool
    max_acceptable_delay_pct: float

The stress calculation adapts to preferences:

if user_avoids_highways:
    # Highways are stressful for this user
    highway_stress = highway_pct * 0.4
    surface_stress = 0
else:
    # Surface streets are more stressful (more complexity)
    highway_stress = highway_pct * 0.1
    surface_stress = surface_pct * 0.25

Familiarity Scoring

Detour tracks routes users have taken and boosts familiar routes in rankings:

familiarity_results = await compute_batch_familiarity(
    db=db,
    user_id=user.id,
    routes=route_data_for_familiarity,
    origin_lat=req.origin.lat,
    origin_lng=req.origin.lng,
    dest_lat=req.destination.lat,
    dest_lng=req.destination.lng,
)

# Familiar routes get tagged and boosted
if fam_result.is_familiar:
    r.tags.append("familiar")

Valhalla Routing Engine

The backend uses Valhalla, an open-source routing engine built on OpenStreetMap data:

valhalla:
  image: ghcr.io/gis-ops/docker-valhalla/valhalla:latest
  environment:
    - tile_urls=https://download.geofabrik.de/north-america/us/michigan-latest.osm.pbf
                https://download.geofabrik.de/north-america/us/nevada-latest.osm.pbf

Multiple costing profiles generate route alternatives:

  • Balanced — Default mix of highways and surface streets
  • Highway Heavy — Prefer interstates
  • No Highway — Avoid highways entirely
  • Shortest — Minimize distance

Traffic Integration

Real-time traffic data from HERE API:

route_traffic = await get_route_traffic(
    polyline=polyline,
    total_distance_m=distance_m,
    base_duration_sec=duration_sec,
)

traffic_data = TrafficConditions(
    overall_severity=route_traffic.overall_severity,
    avg_jam_factor=route_traffic.avg_jam_factor,
    total_delay_sec=route_traffic.total_delay_sec,
    incident_count=route_traffic.incident_count,
)

Flutter App

The mobile app uses:

  • MapLibre GL — Open-source map rendering
  • Geolocator — Device GPS access
  • Google/Apple Sign-In — Authentication
  • Provider — State management

Why I Stopped

The core routing and scoring system works well in the demo. I stopped development because:

  1. Building a production-quality navigation UI is a massive undertaking
  2. Maintaining up-to-date map data requires ongoing infrastructure
  3. The problem space (navigation) is well-served by existing apps

The project was a great learning experience in routing algorithms, geospatial databases, and scoring systems.


Tech Stack

AreaTools
APIFastAPI, Python
RoutingValhalla (OSM)
DatabasePostGIS, SQLAlchemy
TrafficHERE API
MobileFlutter, MapLibre GL
AuthGoogle Sign-In, Apple Sign-In
InfrastructureDocker, Docker Compose