mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:51:08 -05:00
8.2 KiB
8.2 KiB
Location Model Design Document
ParkLocation Model
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
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
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
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
-
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_exitfor road trip searches
- Spatial index on
-
RideLocation:
- Spatial index on
point(PostGIS GiST index) - Index on
park_areafor area-based queries
- Spatial index on
-
CompanyHeadquarters:
- Index on
city - Index on
state - Composite index on (
city,state)
- Index on
OSM Integration Plan
-
Data Collection:
- Store OSM ID in
ParkLocation.osm_id - Cache raw OSM data in
ParkLocation.osm_data
- Store OSM ID in
-
Geocoding:
- Implement Nominatim geocoding service
- Create management command to geocode existing parks
- Add geocoding on ParkLocation save
-
Road Trip Metadata:
- Map OSM highway data to
highway_exitfield - Extract parking information to
parking_notes
- Map OSM highway data to
Migration Strategy
Phase 1: Add New Models
- Create new models (ParkLocation, RideLocation, CompanyHeadquarters)
- Generate migrations
- Deploy to production
Phase 2: Data Migration
-
Migrate existing Location data:
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 ) -
Migrate company headquarters:
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
- Update Park model to use ParkLocation
- Update Ride model to use RideLocation
- Update Company model to use CompanyHeadquarters
- Remove old Location model
Phase 4: OSM Integration
- Implement geocoding command
- Run geocoding for all ParkLocations
- Extract road trip metadata from OSM data
Relationship Diagram
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
- Week 1: Implement models and migrations
- Week 2: Migrate data in staging environment
- Week 3: Deploy to production, migrate data
- Week 4: Implement OSM integration
- Week 5: Optimize queries and indexes