# Location Model Design Document

## ParkLocation Model

```python
from django.contrib.gis.db import models as gis_models
from django.db import models
from parks.models import Park

class ParkLocation(models.Model):
    park = models.OneToOneField(
        Park,
        on_delete=models.CASCADE,
        related_name='location'
    )
    
    # Geographic coordinates
    point = gis_models.PointField(
        srid=4326,  # WGS84 coordinate system
        null=True,
        blank=True,
        help_text="Geographic coordinates as a Point"
    )
    
    # Address components
    street_address = models.CharField(max_length=255, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    state = models.CharField(max_length=100, blank=True, null=True, help_text="State/Region/Province")
    country = models.CharField(max_length=100, blank=True, null=True)
    postal_code = models.CharField(max_length=20, blank=True, null=True)
    
    # Road trip metadata
    highway_exit = models.CharField(
        max_length=100, 
        blank=True, 
        null=True,
        help_text="Nearest highway exit (e.g., 'Exit 42')"
    )
    parking_notes = models.TextField(
        blank=True,
        null=True,
        help_text="Parking information and tips"
    )
    
    # OSM integration
    osm_id = models.BigIntegerField(
        blank=True,
        null=True,
        help_text="OpenStreetMap ID for this location"
    )
    osm_data = models.JSONField(
        blank=True,
        null=True,
        help_text="Raw OSM data snapshot"
    )
    
    class Meta:
        indexes = [
            models.Index(fields=['city']),
            models.Index(fields=['state']),
            models.Index(fields=['country']),
            models.Index(fields=['city', 'state']),
        ]
        # Spatial index will be created automatically by PostGIS
    
    def __str__(self):
        return f"{self.park.name} Location"
    
    @property
    def coordinates(self):
        """Returns coordinates as a tuple (latitude, longitude)"""
        if self.point:
            return (self.point.y, self.point.x)
        return None
    
    def get_formatted_address(self):
        """Returns a formatted address string"""
        components = []
        if self.street_address:
            components.append(self.street_address)
        if self.city:
            components.append(self.city)
        if self.state:
            components.append(self.state)
        if self.postal_code:
            components.append(self.postal_code)
        if self.country:
            components.append(self.country)
        return ", ".join(components) if components else ""
```

## RideLocation Model

```python
from django.contrib.gis.db import models as gis_models
from django.db import models
from parks.models import ParkArea
from rides.models import Ride

class RideLocation(models.Model):
    ride = models.OneToOneField(
        Ride,
        on_delete=models.CASCADE,
        related_name='location'
    )
    
    # Optional coordinates
    point = gis_models.PointField(
        srid=4326,
        null=True,
        blank=True,
        help_text="Precise ride location within park"
    )
    
    # Park area reference
    park_area = models.ForeignKey(
        ParkArea,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='ride_locations'
    )
    
    class Meta:
        indexes = [
            models.Index(fields=['park_area']),
        ]
    
    def __str__(self):
        return f"{self.ride.name} Location"
    
    @property
    def coordinates(self):
        """Returns coordinates as a tuple (latitude, longitude) if available"""
        if self.point:
            return (self.point.y, self.point.x)
        return None
```

## CompanyHeadquarters Model

```python
from django.db import models
from parks.models import Company

class CompanyHeadquarters(models.Model):
    company = models.OneToOneField(
        Company,
        on_delete=models.CASCADE,
        related_name='headquarters'
    )
    
    city = models.CharField(max_length=100)
    state = models.CharField(max_length=100, help_text="State/Region/Province")
    
    class Meta:
        verbose_name_plural = "Company headquarters"
        indexes = [
            models.Index(fields=['city']),
            models.Index(fields=['state']),
            models.Index(fields=['city', 'state']),
        ]
    
    def __str__(self):
        return f"{self.company.name} Headquarters"
```

## Shared Functionality Protocol

```python
from typing import Protocol, Optional, Tuple

class LocationProtocol(Protocol):
    def get_coordinates(self) -> Optional[Tuple[float, float]]:
        """Get coordinates as (latitude, longitude) tuple"""
        ...
    
    def get_location_name(self) -> str:
        """Get human-readable location name"""
        ...
    
    def distance_to(self, other: 'LocationProtocol') -> Optional[float]:
        """Calculate distance to another location in meters"""
        ...
```

## Index Strategy

1. **ParkLocation**:
   - Spatial index on `point` (PostGIS GiST index)
   - Standard indexes on `city`, `state`, `country`
   - Composite index on (`city`, `state`) for common queries
   - Index on `highway_exit` for road trip searches

2. **RideLocation**:
   - Spatial index on `point` (PostGIS GiST index)
   - Index on `park_area` for area-based queries

3. **CompanyHeadquarters**:
   - Index on `city`
   - Index on `state`
   - Composite index on (`city`, `state`)

## OSM Integration Plan

1. **Data Collection**:
   - Store OSM ID in `ParkLocation.osm_id`
   - Cache raw OSM data in `ParkLocation.osm_data`

2. **Geocoding**:
   - Implement Nominatim geocoding service
   - Create management command to geocode existing parks
   - Add geocoding on ParkLocation save

3. **Road Trip Metadata**:
   - Map OSM highway data to `highway_exit` field
   - Extract parking information to `parking_notes`

## Migration Strategy

### Phase 1: Add New Models
1. Create new models (ParkLocation, RideLocation, CompanyHeadquarters)
2. Generate migrations
3. Deploy to production

### Phase 2: Data Migration
1. Migrate existing Location data:
   ```python
   for park in Park.objects.all():
       if park.location.exists():
           loc = park.location.first()
           ParkLocation.objects.create(
               park=park,
               point=loc.point,
               street_address=loc.street_address,
               city=loc.city,
               state=loc.state,
               country=loc.country,
               postal_code=loc.postal_code
           )
   ```
   
2. Migrate company headquarters:
   ```python
   for company in Company.objects.exclude(headquarters=''):
       city, state = parse_headquarters(company.headquarters)
       CompanyHeadquarters.objects.create(
           company=company,
           city=city,
           state=state
       )
   ```

### Phase 3: Update References
1. Update Park model to use ParkLocation
2. Update Ride model to use RideLocation
3. Update Company model to use CompanyHeadquarters
4. Remove old Location model

### Phase 4: OSM Integration
1. Implement geocoding command
2. Run geocoding for all ParkLocations
3. Extract road trip metadata from OSM data

## Relationship Diagram

```mermaid
classDiagram
    Park "1" --> "1" ParkLocation
    Ride "1" --> "1" RideLocation
    Company "1" --> "1" CompanyHeadquarters
    RideLocation "1" --> "0..1" ParkArea
    
    class Park {
        +name: str
    }
    
    class ParkLocation {
        +point: Point
        +street_address: str
        +city: str
        +state: str
        +country: str
        +postal_code: str
        +highway_exit: str
        +parking_notes: str
        +osm_id: int
        +get_coordinates()
        +get_formatted_address()
    }
    
    class Ride {
        +name: str
    }
    
    class RideLocation {
        +point: Point
        +get_coordinates()
    }
    
    class Company {
        +name: str
    }
    
    class CompanyHeadquarters {
        +city: str
        +state: str
    }
    
    class ParkArea {
        +name: str
    }
```

## Rollout Timeline

1. **Week 1**: Implement models and migrations
2. **Week 2**: Migrate data in staging environment
3. **Week 3**: Deploy to production, migrate data
4. **Week 4**: Implement OSM integration
5. **Week 5**: Optimize queries and indexes