mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:11:09 -05:00
Refactor advanced search template to utilize Alpine.js for state management. Enhance search functionality with dynamic view modes and improved filter handling using HTMX.
145 lines
4.8 KiB
Python
145 lines
4.8 KiB
Python
# from django.contrib.gis.db import models # Disabled temporarily for setup
|
|
from django.db import models
|
|
# from django.contrib.gis.geos import Point # Disabled temporarily for setup
|
|
import pghistory
|
|
from apps.core.history import TrackedModel
|
|
|
|
|
|
@pghistory.track()
|
|
class ParkLocation(TrackedModel):
|
|
"""
|
|
Represents the geographic location and address of a park, with PostGIS support.
|
|
"""
|
|
|
|
park = models.OneToOneField(
|
|
"parks.Park", on_delete=models.CASCADE, related_name="location"
|
|
)
|
|
|
|
# Spatial Data - Temporarily disabled for setup
|
|
# point = models.PointField(
|
|
# srid=4326,
|
|
# null=True,
|
|
# blank=True,
|
|
# help_text="Geographic coordinates (longitude, latitude)",
|
|
# )
|
|
point = models.CharField(max_length=50, null=True, blank=True) # Temporary placeholder
|
|
|
|
# Address Fields
|
|
street_address = models.CharField(max_length=255, blank=True)
|
|
city = models.CharField(max_length=100, db_index=True)
|
|
state = models.CharField(max_length=100, db_index=True)
|
|
country = models.CharField(max_length=100, default="USA")
|
|
continent = models.CharField(
|
|
max_length=50,
|
|
blank=True,
|
|
db_index=True,
|
|
help_text="Continent where the park is located"
|
|
)
|
|
postal_code = models.CharField(max_length=20, blank=True)
|
|
|
|
# Road Trip Metadata
|
|
highway_exit = models.CharField(max_length=100, blank=True)
|
|
parking_notes = models.TextField(blank=True)
|
|
best_arrival_time = models.TimeField(null=True, blank=True)
|
|
seasonal_notes = models.TextField(blank=True)
|
|
|
|
# OSM Integration
|
|
osm_id = models.BigIntegerField(null=True, blank=True)
|
|
osm_type = models.CharField(
|
|
max_length=10,
|
|
blank=True,
|
|
help_text="Type of OpenStreetMap object (node, way, or relation)",
|
|
)
|
|
|
|
@property
|
|
def latitude(self):
|
|
"""Return latitude from point field."""
|
|
if self.point and ',' in self.point:
|
|
# Temporary string format: "longitude,latitude"
|
|
return float(self.point.split(',')[1])
|
|
return None
|
|
|
|
@property
|
|
def longitude(self):
|
|
"""Return longitude from point field."""
|
|
if self.point and ',' in self.point:
|
|
# Temporary string format: "longitude,latitude"
|
|
return float(self.point.split(',')[0])
|
|
return None
|
|
|
|
@property
|
|
def coordinates(self):
|
|
"""Return (latitude, longitude) tuple."""
|
|
if self.point:
|
|
return (self.latitude, self.longitude)
|
|
return (None, None)
|
|
|
|
@property
|
|
def formatted_address(self):
|
|
"""Return a nicely formatted address string."""
|
|
address_parts = [
|
|
self.street_address,
|
|
self.city,
|
|
self.state,
|
|
self.postal_code,
|
|
self.country,
|
|
]
|
|
return ", ".join(part for part in address_parts if part)
|
|
|
|
def set_coordinates(self, latitude, longitude):
|
|
"""
|
|
Set the location's point from latitude and longitude coordinates.
|
|
Validates coordinate ranges.
|
|
"""
|
|
if latitude is None or longitude is None:
|
|
self.point = None
|
|
return
|
|
|
|
if not -90 <= latitude <= 90:
|
|
raise ValueError("Latitude must be between -90 and 90.")
|
|
if not -180 <= longitude <= 180:
|
|
raise ValueError("Longitude must be between -180 and 180.")
|
|
|
|
# Temporarily store as string until PostGIS is enabled
|
|
self.point = f"{longitude},{latitude}"
|
|
# self.point = Point(longitude, latitude, srid=4326)
|
|
|
|
def distance_to(self, other_location):
|
|
"""
|
|
Calculate the distance to another ParkLocation instance.
|
|
Returns distance in kilometers.
|
|
"""
|
|
if not self.point or not other_location.point:
|
|
return None
|
|
|
|
# Temporary implementation using Haversine formula
|
|
# TODO: Replace with PostGIS distance calculation when enabled
|
|
import math
|
|
|
|
lat1, lon1 = self.latitude, self.longitude
|
|
lat2, lon2 = other_location.latitude, other_location.longitude
|
|
|
|
if None in (lat1, lon1, lat2, lon2):
|
|
return None
|
|
|
|
# Haversine formula
|
|
R = 6371 # Earth's radius in kilometers
|
|
dlat = math.radians(lat2 - lat1)
|
|
dlon = math.radians(lon2 - lon1)
|
|
a = (math.sin(dlat/2) * math.sin(dlat/2) +
|
|
math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
|
|
math.sin(dlon/2) * math.sin(dlon/2))
|
|
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
|
return R * c
|
|
|
|
def __str__(self):
|
|
return f"Location for {self.park.name}"
|
|
|
|
class Meta:
|
|
verbose_name = "Park Location"
|
|
verbose_name_plural = "Park Locations"
|
|
ordering = ["park__name"]
|
|
indexes = [
|
|
models.Index(fields=["city", "state"]),
|
|
]
|