Files
thrillwiki_django_no_react/shared/docs/memory-bank/features/roadtrip-service-documentation.md
pacnpal d504d41de2 feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
2025-08-23 18:40:07 -04:00

10 KiB

OSM Road Trip Service Documentation

Overview

The OSM Road Trip Service provides comprehensive road trip planning functionality for theme parks using free OpenStreetMap APIs. It enables users to plan routes between parks, find parks along routes, and optimize multi-park trips.

Features Implemented

1. Core Service Architecture

Location: parks/services/roadtrip.py

The service is built around the RoadTripService class which provides all road trip planning functionality with proper error handling, caching, and rate limiting.

2. Geocoding Service

Uses Nominatim (OpenStreetMap's geocoding service) to convert addresses to coordinates:

from parks.services import RoadTripService

service = RoadTripService()
coords = service.geocode_address("Cedar Point, Sandusky, Ohio")
# Returns: Coordinates(latitude=41.4826, longitude=-82.6862)

Features:

  • Converts any address string to latitude/longitude coordinates
  • Automatic caching of geocoding results (24-hour cache)
  • Proper error handling for invalid addresses
  • Rate limiting (1 request per second)

3. Route Calculation

Uses OSRM (Open Source Routing Machine) for route calculation with fallback to straight-line distance:

from parks.services.roadtrip import Coordinates

start = Coordinates(41.4826, -82.6862)  # Cedar Point
end = Coordinates(28.4177, -81.5812)    # Magic Kingdom

route = service.calculate_route(start, end)
# Returns: RouteInfo(distance_km=1745.7, duration_minutes=1244, geometry="encoded_polyline")

Features:

  • Real driving routes with distance and time estimates
  • Encoded polyline geometry for route visualization
  • Fallback to straight-line distance when routing fails
  • Route caching (6-hour cache)
  • Graceful error handling

4. Park Integration

Seamlessly integrates with existing Park and ParkLocation models:

# Geocode parks that don't have coordinates
park = Park.objects.get(name="Some Park")
success = service.geocode_park_if_needed(park)

# Get park coordinates
coords = park.coordinates  # Returns (lat, lon) tuple or None

Features:

  • Automatic geocoding for parks without coordinates
  • Uses existing PostGIS PointField infrastructure
  • Respects existing location data structure

5. Route Discovery

Find parks along a specific route within a detour distance:

start_park = Park.objects.get(name="Cedar Point")
end_park = Park.objects.get(name="Magic Kingdom")

parks_along_route = service.find_parks_along_route(
    start_park, 
    end_park, 
    max_detour_km=50
)

Features:

  • Finds parks within specified detour distance
  • Calculates actual detour cost (not just proximity)
  • Uses PostGIS spatial queries for efficiency

6. Nearby Park Discovery

Find all parks within a radius of a center park:

center_park = Park.objects.get(name="Disney World")
nearby_parks = service.get_park_distances(center_park, radius_km=100)

# Returns list of dicts with park, distance, and duration info
for result in nearby_parks:
    print(f"{result['park'].name}: {result['formatted_distance']}")

Features:

  • Finds parks within specified radius
  • Returns actual driving distances and times
  • Sorted by distance
  • Formatted output for easy display

7. Multi-Park Trip Planning

Plan optimized routes for visiting multiple parks:

parks_to_visit = [park1, park2, park3, park4]
trip = service.create_multi_park_trip(parks_to_visit)

print(f"Total Distance: {trip.formatted_total_distance}")
print(f"Total Duration: {trip.formatted_total_duration}")

for leg in trip.legs:
    print(f"{leg.from_park.name}{leg.to_park.name}: {leg.route.formatted_distance}")

Features:

  • Optimizes route order using traveling salesman heuristics
  • Exhaustive search for small groups (≤6 parks)
  • Nearest neighbor heuristic for larger groups
  • Returns detailed leg-by-leg information
  • Total trip statistics

API Configuration

Django Settings

Added to thrillwiki/settings.py:

# Road Trip Service Settings
ROADTRIP_CACHE_TIMEOUT = 3600 * 24  # 24 hours for geocoding
ROADTRIP_ROUTE_CACHE_TIMEOUT = 3600 * 6  # 6 hours for routes
ROADTRIP_MAX_REQUESTS_PER_SECOND = 1  # Respect OSM rate limits
ROADTRIP_USER_AGENT = "ThrillWiki Road Trip Planner (https://thrillwiki.com)"
ROADTRIP_REQUEST_TIMEOUT = 10  # seconds
ROADTRIP_MAX_RETRIES = 3
ROADTRIP_BACKOFF_FACTOR = 2

External APIs Used

  1. Nominatim Geocoding: https://nominatim.openstreetmap.org/search

    • Free OpenStreetMap geocoding service
    • Rate limit: 1 request per second
    • Returns JSON with lat/lon coordinates
  2. OSRM Routing: http://router.project-osrm.org/route/v1/driving/

    • Free routing service for driving directions
    • Returns distance, duration, and route geometry
    • Fallback to straight-line distance if unavailable

Data Models

Core Data Classes

@dataclass
class Coordinates:
    latitude: float
    longitude: float

@dataclass
class RouteInfo:
    distance_km: float
    duration_minutes: int
    geometry: Optional[str] = None  # Encoded polyline

@dataclass
class RoadTrip:
    parks: List[Park]
    legs: List[TripLeg]
    total_distance_km: float
    total_duration_minutes: int

Integration Points

  • Park Model: Access via park.coordinates property
  • ParkLocation Model: Uses point PointField for spatial data
  • Django Cache: Automatic caching of API results
  • PostGIS: Spatial queries for nearby park discovery

Performance & Caching

Caching Strategy

  1. Geocoding Results: 24-hour cache

    • Cache key: roadtrip:geocode:{hash(address)}
    • Reduces redundant API calls for same addresses
  2. Route Calculations: 6-hour cache

    • Cache key: roadtrip:route:{start_coords}:{end_coords}
    • Balances freshness with API efficiency

Rate Limiting

  • 1 request per second to respect OSM usage policies
  • Automatic rate limiting between API calls
  • Exponential backoff for failed requests
  • User-Agent identification as required by OSM

Error Handling

Graceful Degradation

  1. Network Issues: Retry with exponential backoff
  2. Invalid Coordinates: Fall back to straight-line distance
  3. Geocoding Failures: Return None, don't crash
  4. Missing Location Data: Skip parks without coordinates
  5. API Rate Limits: Automatic waiting and retry

Logging

Comprehensive logging for debugging and monitoring:

  • Successful geocoding/routing operations
  • API failures and retry attempts
  • Cache hits and misses
  • Rate limiting activation

Testing

Test Suite

Location: test_roadtrip_service.py

Comprehensive test suite covering:

  • Geocoding functionality
  • Route calculation
  • Park integration
  • Multi-park trip planning
  • Error handling
  • Rate limiting
  • Cache functionality

Test Results Summary

  • Geocoding: Successfully geocodes theme park addresses
  • Routing: Calculates accurate routes with OSRM
  • Caching: Properly caches results to minimize API calls
  • Rate Limiting: Respects 1 req/sec limit
  • Trip Planning: Optimizes multi-park routes
  • Error Handling: Gracefully handles failures
  • Integration: Works with existing Park/ParkLocation models

Usage Examples

Basic Geocoding and Routing

from parks.services import RoadTripService

service = RoadTripService()

# Geocode an address
coords = service.geocode_address("Universal Studios, Orlando, FL")

# Calculate route between two points
from parks.services.roadtrip import Coordinates
start = Coordinates(28.4755, -81.4685)  # Universal
end = Coordinates(28.4177, -81.5812)    # Magic Kingdom

route = service.calculate_route(start, end)
print(f"Distance: {route.formatted_distance}")
print(f"Duration: {route.formatted_duration}")

Working with Parks

# Find nearby parks
disney_world = Park.objects.get(name="Magic Kingdom")
nearby = service.get_park_distances(disney_world, radius_km=50)

for result in nearby[:5]:
    park = result['park']
    print(f"{park.name}: {result['formatted_distance']} away")

# Plan a multi-park trip
florida_parks = [
    Park.objects.get(name="Magic Kingdom"),
    Park.objects.get(name="SeaWorld Orlando"),
    Park.objects.get(name="Universal Studios Florida"),
]

trip = service.create_multi_park_trip(florida_parks)
print(f"Optimized trip: {trip.formatted_total_distance}")

Find Parks Along Route

start_park = Park.objects.get(name="Cedar Point")
end_park = Park.objects.get(name="Kings Island")

# Find parks within 25km of the route
parks_along_route = service.find_parks_along_route(
    start_park, 
    end_park, 
    max_detour_km=25
)

print(f"Found {len(parks_along_route)} parks along the route")

OSM Usage Compliance

Respectful API Usage

  • Proper User-Agent: Identifies application and contact info
  • Rate Limiting: 1 request per second as recommended
  • Caching: Minimizes redundant API calls
  • Error Handling: Doesn't spam APIs when they fail
  • Attribution: Service credits OpenStreetMap data

Terms Compliance

  • Uses free OSM services within their usage policies
  • Provides proper attribution for OpenStreetMap data
  • Implements reasonable rate limiting
  • Graceful fallbacks when services unavailable

Future Enhancements

Potential Improvements

  1. Alternative Routing Providers

    • GraphHopper integration as OSRM backup
    • Mapbox Directions API for premium users
  2. Advanced Trip Planning

    • Time-based optimization (opening hours, crowds)
    • Multi-day trip planning with hotels
    • Seasonal route recommendations
  3. Performance Optimizations

    • Background geocoding of new parks
    • Precomputed distance matrices for popular parks
    • Redis caching for high-traffic scenarios
  4. User Features

    • Save and share trip plans
    • Export to GPS devices
    • Integration with calendar apps

Dependencies

  • requests: HTTP client for API calls
  • Django GIS: PostGIS integration for spatial queries
  • Django Cache: Built-in caching framework

All dependencies are managed via UV package manager as per project standards.