Skip to content
Merged
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: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Wayfarer Mobile is a privacy-first companion app for self-hosted Wayfarer server

- **Offline-First Architecture**: Local SQLite storage with background sync, works without internet
- **Smart Battery Usage**: Three-phase sleep/wake optimization for background tracking (~1-3% per hour)
- **Dual Navigation Modes**: Trip navigation (user segments → cached → OSRM → direct) and ad-hoc navigation (OSRM → direct)
- **Truthful Navigation**: Saved Trip Segment geometry when available, otherwise straight-line Direct guidance
- **Queue Resilience**: Configurable queue limit (default 25,000), fast sync (12s/location), export to CSV/GeoJSON

> **Map cache note**: OpenStreetMap tiles requested during interactive viewing are kept in a bounded live cache. Previously viewed tiles may remain usable while cached, but Trip downloads do not include or guarantee an offline basemap area.
Expand Down Expand Up @@ -133,7 +133,7 @@ WayfarerMobile/
|----------|------------|
| Framework | .NET 10 MAUI |
| Maps | Mapsui 5.1 with OpenStreetMap tiles |
| Routing | OSRM (Open Source Routing Machine) |
| Navigation | Saved Segment geometry and Direct guidance |
| UI Components | Syncfusion MAUI Toolkit (MIT) |
| MVVM | CommunityToolkit.Mvvm |
| Database | SQLite-net-pcl |
Expand Down
12 changes: 0 additions & 12 deletions THIRD-PARTY-NOTICES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -259,18 +259,6 @@ https://opendatacommons.org/licenses/odbl/
Note: This application uses a local tile cache to respect OpenStreetMap's
fair use policy. See https://operations.osmfoundation.org/policies/tiles/

--------------------------------------------------------------------------------

OSRM (Open Source Routing Machine)
----------------------------------
https://project-osrm.org/
https://github.com/Project-OSRM/osrm-backend
Copyright (c) Project OSRM contributors

Licensed under the BSD 2-Clause License.

Note: Routing can be provided by an OSRM-compatible endpoint (demo or self-hosted).

================================================================================

LICENSE TEXTS
Expand Down
29 changes: 10 additions & 19 deletions docs/03-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ From the main map, you can add your current location to the loaded trip as a new

---

## Turn-by-Turn Navigation
## Navigation

Navigate to destinations with intelligent routing that adapts to context.
Navigate with saved Trip Segment geometry or honest straight-line Direct guidance. Mobile does not contact a public routing provider.

### Navigation Contexts

Expand All @@ -310,8 +310,8 @@ The app supports navigation in different contexts:
| Context | Started From | Features |
|---------|--------------|----------|
| **Trip Navigation** | Trip sidebar → place | Uses trip segments, full route priority |
| **Group Navigation** | Groups → member | OSRM routing to member location |
| **Map Navigation** | Long-press on map | OSRM routing to any point |
| **Group Navigation** | Groups → member | Direct guidance to member location |
| **Map Navigation** | Long-press on map | Direct guidance to any point |

### Starting Trip Navigation

Expand Down Expand Up @@ -343,32 +343,24 @@ Route calculation differs based on navigation context:
| Priority | Source | When Used |
|----------|--------|-----------|
| 1 | **User Segments** | Trip has pre-defined route geometry |
| 2 | **Cached OSRM** | Valid cache exists (same dest, <50m origin, <5 min old) |
| 3 | **OSRM Fetch** | Online and no cache available |
| 4 | **Direct Route** | Offline fallback (straight-line with bearing) |
| 2 | **Direct Route** | Saved geometry is unavailable or invalid |

**Ad-Hoc Navigation** (groups, map locations):
| Priority | Source | When Used |
|----------|--------|-----------|
| 1 | **OSRM Fetch** | Online route calculation |
| 2 | **Direct Route** | Offline fallback (straight-line with bearing) |
| 1 | **Direct Route** | Always; ad-hoc targets have no saved Segment geometry |

> **Note**: Ad-hoc navigation doesn't have user segments or route caching since there's no trip context.
> **Note**: Ad-hoc navigation does not have saved Segment geometry because there is no Trip context.

**User Segments**: Routes you defined when planning the trip. These include the exact polyline geometry and are always preferred over calculated routes.

**Cached OSRM**: Previously fetched routes are cached and reused if:
- Same destination
- Origin within 50 meters of cached origin
- Less than 5 minutes old

**OSRM Fetch**: Online route calculation from OSRM (Open Source Routing Machine). Supports walking, driving, and cycling profiles. Rate limited to 1 request per second.

**Direct Route**: When offline and no cached route exists, shows straight-line navigation with:
**Direct Route**: When saved Segment geometry is unavailable or invalid, shows straight-line navigation with:
- Cardinal direction (N, NE, E, etc.)
- Distance to destination
- Bearing-based heading

Direct is not road-aware or hosted turn-by-turn routing. Authenticated Wayfarer-hosted routing is planned separately and is not implemented yet.

### External Maps Integration

For any navigation, you can choose **External Maps** to hand off to:
Expand Down Expand Up @@ -657,7 +649,6 @@ For troubleshooting, access detailed diagnostics:
- **Location Queue**: Pending sync items
- **Tile Cache**: Cache statistics
- **Tracking**: Service status
- **Navigation**: Route cache info
3. Export diagnostic report for support

---
Expand Down
2 changes: 1 addition & 1 deletion docs/04-Trips-and-Offline.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The live cache can be inspected and cleared from **Settings** > **Map Cache**. C

## Using Trip Content Offline

Without a network connection, downloaded Places, Segments, Areas, and Trip metadata remain available. Planned Segment geometry is preferred for navigation. A valid cached route can also be used; otherwise navigation provides an honest direct distance and bearing fallback when online routing is unavailable.
Without a network connection, downloaded Places, Segments, Areas, and Trip metadata remain available. Valid planned Segment geometry is preferred for navigation; otherwise navigation provides honest Direct distance and bearing guidance. Direct is straight-line guidance, not road-aware turn-by-turn routing.

Timeline data, queued locations, pending mutations, authentication state, and ordinary synchronization are independent of Trip downloads and the interactive map cache.

Expand Down
5 changes: 3 additions & 2 deletions docs/08-FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,9 @@ Full trip creation (defining regions, creating route segments, detailed planning

Navigation only shows detailed routes if:
- The trip has segments defined (created on web)
- Or OSRM can calculate a route (requires internet)
- Without either, you get direct bearing/distance guidance
- Without valid saved Segment geometry, you get Direct bearing/distance guidance

Direct guidance is a straight line, not road-aware turn-by-turn routing. Mobile does not contact a public routing provider.

### What's the 50-meter rule?

Expand Down
2 changes: 1 addition & 1 deletion docs/09-Developer-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ WayfarerMobile is a .NET MAUI cross-platform mobile application for location tra

- **Background Location Tracking**: 24/7 location tracking using platform-native foreground services
- **Offline Trip Content**: Store Trip metadata, Places, routes, Areas, and navigation data in SQLite
- **Turn-by-Turn Navigation**: OSRM-based routing with audio announcements
- **Navigation**: Offline saved Segment geometry and provider-independent Direct guidance with applicable audio announcements
- **Group Location Sharing**: Real-time location sharing via Server-Sent Events (SSE)
- **PIN Security**: Optional app lock with salted SHA256 PIN hashing
- **Timeline History**: View and manage location history synchronized with the server
Expand Down
15 changes: 5 additions & 10 deletions docs/11-Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ public class SettingsService : ISettingsService
| **API** | `ApiClient`, `GroupsService`, `GroupMemberManager` |
| **Sync** | `QueueDrainService`, `TripSyncCoordinator`, `TimelineSyncService`, `SyncEventBus` |
| **Maps** | `MapBuilder`, `LocationLayerService`, `TripLayerService`, `GroupLayerService`, `TimelineLayerService`, `DroppedPinLayerService` |
| **Navigation** | `TripNavigationService`, `OsrmRoutingService`, `RouteCacheService` |
| **Navigation** | `TripNavigationService`, `NavigationRouteBuilder` |
| **Interactive map cache** | `WayfarerTileSource`, `LiveTileCacheService`, `LiveTileCacheRepository` |
| **Trip** | `TripStateManager`, `TripContentService`, `TripMetadataBuilder`, `PlaceOperationsHandler`, `RegionOperationsHandler` |
| **Timeline** | `TimelineDataService`, `LocalTimelineStorageService`, `MutationQueueService` |
Expand All @@ -419,11 +419,6 @@ services.AddHttpClient("WayfarerApi", client =>
new MediaTypeWithQualityHeaderValue("application/json"));
});

services.AddHttpClient("Osrm", client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("User-Agent", "WayfarerMobile/1.0");
});
```

## Navigation System
Expand All @@ -432,10 +427,10 @@ services.AddHttpClient("Osrm", client =>

The `TripNavigationService` calculates routes with the following priority:

1. **User Segments**: Trip-defined routes with polyline geometry (always preferred)
2. **Cached OSRM**: Previously fetched route if still valid
3. **OSRM Fetch**: Online route from `router.project-osrm.org`
4. **Direct Route**: Straight line with bearing + distance (offline fallback)
1. **Saved Segment geometry**: Trip-defined geometry (always preferred when valid)
2. **Direct guidance**: Straight line with bearing and distance

Mobile does not contact a public routing provider. Authenticated Wayfarer-hosted routing is future work and is not part of the current architecture.

### Navigation Graph

Expand Down
119 changes: 12 additions & 107 deletions docs/12-Services.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,8 @@ This document provides detailed documentation for the key services in WayfarerMo

| Service | Purpose | Rate Limit |
|---------|---------|------------|
| `TripNavigationService` | Route calculation, turn-by-turn | N/A |
| `OsrmRoutingService` | OSRM API client | 1 req/second |
| `RouteCacheService` | Single-route session cache | N/A |
| `TripNavigationService` | Saved-geometry and Direct route calculation | N/A |
| `NavigationRouteBuilder` | Saved Segment and Direct route construction | N/A |
| `NavigationAudioService` | Voice announcements | N/A |

### Data Services
Expand Down Expand Up @@ -489,32 +488,29 @@ Manages the dropped pin marker for map long-press interactions. Stateless render

**Source**: `src/WayfarerMobile/Services/TripNavigationService.cs`

Provides navigation with route calculation and progress tracking. Supports two modes:
Provides navigation with route calculation and progress tracking. Mobile makes no direct routing-provider request.

### Navigation Modes

**Trip Navigation** (`CalculateRouteToPlaceAsync`):
- Used when navigating to a trip place
- Has access to user-defined segments and trip context
- Route priority:
1. User Segments (trip-defined routes)
2. Cached OSRM (valid cache)
3. OSRM Fetch (online)
4. Direct Route (offline fallback)
1. Valid saved Segment geometry (trip-defined routes)
2. Direct Route (straight-line fallback)

**Ad-Hoc Navigation** (`CalculateRouteToCoordinatesAsync`):
- Used for groups, map locations, any coordinates
- No trip context available
- Route priority:
1. OSRM Fetch (online)
2. Direct Route (offline fallback)
1. Direct Route

```csharp
// Trip navigation - uses full route priority chain
var route = await _tripNavigationService.CalculateRouteToPlaceAsync(
currentLat, currentLon, destinationPlaceId);

// Ad-hoc navigation - OSRM or direct only
// Ad-hoc navigation - Direct straight-line guidance
var route = await _tripNavigationService.CalculateRouteToCoordinatesAsync(
currentLat, currentLon, destLat, destLon, destName, profile: "foot");
```
Expand All @@ -535,10 +531,9 @@ public class TripNavigationGraph
### Route Calculation

```csharp
public async Task<NavigationRoute?> CalculateRouteToPlaceAsync(
public NavigationRoute? CalculateRouteToPlace(
double currentLat, double currentLon,
string destinationPlaceId,
bool fetchFromOsrm = true)
string destinationPlaceId)
{
// Priority 1: User-defined segment
if (_currentGraph.IsWithinSegmentRoutingRange(currentLat, currentLon))
Expand All @@ -548,27 +543,13 @@ public async Task<NavigationRoute?> CalculateRouteToPlaceAsync(
return BuildRouteFromPath(path, currentLat, currentLon);
}

// Priority 2: Cached OSRM route
var cachedRoute = _routeCacheService.GetValidRoute(currentLat, currentLon, destinationPlaceId);
if (cachedRoute != null)
return BuildRouteFromCache(cachedRoute, ...);

// Priority 3: OSRM fetch
if (fetchFromOsrm)
{
var osrmRoute = await _osrmService.GetRouteAsync(...);
if (osrmRoute != null)
{
_routeCacheService.SaveRoute(...);
return BuildRouteFromOsrm(osrmRoute, ...);
}
}

// Priority 4: Direct route
// Priority 2: Direct route
return BuildDirectRoute(currentLat, currentLon, destination);
}
```

Direct guidance is not road-aware or hosted turn-by-turn routing. Authenticated Wayfarer-hosted routing remains future work.

### Navigation State

```csharp
Expand Down Expand Up @@ -1057,82 +1038,6 @@ Manages activity types with server sync and local caching.

---

## OsrmRoutingService

**Source**: `src/WayfarerMobile/Services/OsrmRoutingService.cs`

OSRM (Open Source Routing Machine) API client for route calculation.

### Configuration

| Setting | Value |
|---------|-------|
| Base URL | `https://router.project-osrm.org` |
| Rate limit | 1 request/second |
| Timeout | 10 seconds |
| Profiles | foot, car, bike |

### Rate Limiting

```csharp
private static readonly TimeSpan MinRequestInterval = TimeSpan.FromSeconds(1.1);

private static async Task EnforceRateLimitAsync()
{
var timeSinceLastRequest = DateTime.UtcNow - _lastRequestTime;
if (timeSinceLastRequest < MinRequestInterval)
{
await Task.Delay(MinRequestInterval - timeSinceLastRequest);
}
_lastRequestTime = DateTime.UtcNow;
}
```

### Response

```csharp
public class OsrmRouteResult
{
public string Geometry { get; set; } // Encoded polyline
public double DistanceMeters { get; set; }
public double DurationSeconds { get; set; }
public List<OsrmStepResult> Steps { get; set; } // Turn instructions
}
```

---

## RouteCacheService

**Source**: `src/WayfarerMobile/Services/RouteCacheService.cs`

Single-route session cache stored in Preferences. Survives app restart.

### Cache Validity

A cached route is valid if:
- Same destination place ID
- Origin within **50 meters** of cached origin
- Less than **5 minutes** old

### Storage

```csharp
public class CachedRoute
{
public string DestinationPlaceId { get; set; }
public string DestinationName { get; set; }
public double OriginLatitude { get; set; }
public double OriginLongitude { get; set; }
public string Geometry { get; set; } // Encoded polyline
public double DistanceMeters { get; set; }
public double DurationSeconds { get; set; }
public DateTime FetchedAtUtc { get; set; }
}
```

---

## Interactive OSM Map Cache

`WayfarerTileSource` requests the canonical OpenStreetMap layer as the renderer pans and zooms. `LiveTileCacheService` serves fresh entries without HTTP, conditionally revalidates expired entries, and keeps the live cache bounded by least-recently-used cleanup. Distinct visible tiles are not globally serialized.
Expand Down
Loading
Loading