Add all local circuit work — year-by-year SVGs and tooling

This commit is contained in:
2026-03-25 22:17:47 +01:00
parent 6ceeba7fb9
commit c94ebcfb22
1149 changed files with 171650 additions and 421 deletions
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+23
View File
@@ -0,0 +1,23 @@
from dataclasses import dataclass
from typing import Optional, List, Dict, Any, TYPE_CHECKING
from models.geo_json.geometry import GeoJSONGeometry
from models.geo_json.properties import GeoJSONProperties
if TYPE_CHECKING:
from models.circuit import Circuit
@dataclass
class GeoJSONFeature:
type: str
properties: GeoJSONProperties
bbox: Optional[List[float]]
geometry: GeoJSONGeometry
@classmethod
def from_dict(cls, circuit: 'Circuit', data: Dict[str, Any]) -> 'GeoJSONFeature':
return cls(
type=data['type'],
properties=GeoJSONProperties.from_dict(circuit, data['properties']),
geometry=GeoJSONGeometry.from_dict(data['geometry']),
bbox=data.get('bbox')
)
+27
View File
@@ -0,0 +1,27 @@
from dataclasses import dataclass, asdict
from typing import List, Dict, Any, TYPE_CHECKING, Optional
from models.geo_json.feature import GeoJSONFeature
if TYPE_CHECKING:
from models.circuit import Circuit
@dataclass
class GeoJSON:
type: str
bbox: Optional[list[float]]
features: List[GeoJSONFeature]
@classmethod
def from_dict(cls, circuit: 'Circuit', data: Dict[str, Any]) -> 'GeoJSON':
return cls(
type=data['type'],
bbox=data['bbox'] if 'bbox' in data else None,
features=[GeoJSONFeature.from_dict(circuit, f) for f in data['features']],
)
def to_dict(self) -> Dict[str, Any]:
"""Convert the GeoJSON object back to a dictionary"""
data = asdict(self)
# Remove None values
return {k: v for k, v in data.items() if v is not None}
+56
View File
@@ -0,0 +1,56 @@
from dataclasses import dataclass
from typing import List, Dict, Any
import numpy as np
@dataclass
class GeoJSONGeometry:
type: str
coordinates: List[tuple[float, float]]
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'GeoJSONGeometry':
return cls(
type=data['type'],
coordinates=data['coordinates']
)
def find_longest_straight(self, window_size=5) -> (List[float], List[float]):
"""Find the longest approximately straight section of the track, including wrap-around."""
max_distance = 0
best_start_idx = 0
n = len(self.coordinates)
# Helper function to check straightness
def is_straight(points, start, end, length):
direction = (end - start) / length
distances = []
for point in points:
projection = start + np.dot(point - start, direction) * direction
distance = np.linalg.norm(point - projection)
distances.append(distance)
return max(distances) < length * 0.05 # 5% tolerance
# Check all possible segments, including wrap-around
for i in range(n):
# Get window_size points, handling wrap-around
segment = []
for j in range(window_size):
idx = (i + j) % n
segment.append(np.array(self.coordinates[idx]))
start = np.array(segment[0])
end = np.array(segment[-1])
length = np.linalg.norm(end - start)
if length > max_distance:
# Check if all points are roughly on the line
if is_straight(segment, start, end, length):
max_distance = length
best_start_idx = i
# Return indices that might wrap around
end_idx = (best_start_idx + window_size) % len(self.coordinates)
return self.coordinates[best_start_idx], self.coordinates[end_idx]
+29
View File
@@ -0,0 +1,29 @@
from dataclasses import dataclass
from typing import Optional, Dict, Any, TYPE_CHECKING
if TYPE_CHECKING:
from models.circuit import Circuit
@dataclass
class GeoJSONProperties:
id: str
country: str
locality: str
circuit: str
length: Optional[int] = None
altitude: Optional[int] = None
rotation: Optional[float] = None
direction: Optional[str] = None
fastest_laptime: Optional[float] = None
@classmethod
def from_dict(cls, circuit: 'Circuit', data: Dict[str, Any]) -> 'GeoJSONProperties':
return cls(
id=data.get("id"),
country=circuit.locality.country.name,
locality=circuit.locality.name,
circuit=circuit.name,
**{k: v for k, v in data.items() if
k in cls.__annotations__ and k not in ['id', 'country', 'locality',
'circuit']}
)