import os import json import matplotlib.pyplot as plt from dataclasses import dataclass from typing import List, Dict, Any, Optional, Tuple from pathlib import Path @dataclass class Circuit: """Model class representing an F1 circuit.""" id: str name: str location: str opened: Optional[int] = None first_gp: Optional[int] = None length: Optional[int] = None # in meters altitude: Optional[int] = None coordinates: List[Tuple[float, float]] = None bbox: Optional[List[float]] = None def __post_init__(self): if self.coordinates is None: self.coordinates = [] def plot(self): """Generate a simple plot of the circuit with transparent background and no grid/axes.""" if not self.coordinates: print(f"No coordinates available for {self.name}") return # Extract longitude and latitude from coordinates lons, lats = zip(*self.coordinates) # Create figure with transparent background fig = plt.figure(figsize=(10, 8), facecolor="none") ax = fig.add_subplot(111) # Plot the circuit with black line ax.plot(lons, lats, 'k-', linewidth=2) # Remove grid, axes, and title ax.grid(False) ax.set_xticks([]) ax.set_yticks([]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['bottom'].set_visible(False) ax.spines['left'].set_visible(False) # Keep aspect ratio equal ax.set_aspect('equal') # Add padding around the circuit ax.margins(0.1) # Tight layout plt.tight_layout() return fig def create_empty_geojson(circuit_name: str, location: str, file_path: Path): """Create an empty GeoJSON template file""" template = { "type": "FeatureCollection", "features": [{ "type": "Feature", "properties": { "Name": circuit_name, "Location": location, "opened": None, "firstgp": None, "length": None, "altitude": None }, "geometry": { "type": "LineString", "coordinates": [] } }] } file_path.parent.mkdir(parents=True, exist_ok=True) with open(file_path, 'w', encoding='utf-8') as f: json.dump(template, f, indent=2) print(f"Created empty GeoJSON template: {file_path}") def load_circuits_json() -> dict: """Load the circuits.json file""" with open("circuits.json", 'r', encoding='utf-8') as f: return json.load(f) def process_circuit_files(): """Process all circuit files based on circuits.json data""" data = load_circuits_json() for country, country_data in data.items(): country_slug = country_data['slug'] for city, city_data in country_data['cities'].items(): city_slug = city_data['slug'] for circuit_name, circuit_data in city_data['circuits'].items(): circuit_slug = circuit_data['slug'] if 'layouts' in circuit_data: for layout_years, layout_data in circuit_data['layouts'].items(): layout_slug = layout_data['slug'] # Construct file paths base_path = Path(f"{country_slug}/{city_slug}/{circuit_slug}") geo_path = base_path / f"{layout_slug}.geo.json" svg_path = base_path / f"{layout_slug}.svg" png_path = base_path / f"{layout_slug}.png" # Create empty GeoJSON if missing if not geo_path.exists(): create_empty_geojson(circuit_name, f"{city}, {country}", geo_path) continue # Skip SVG/PNG generation as we have no coordinates # Parse existing GeoJSON and generate SVG/PNG if needed circuit = parse_geojson(geo_path) if circuit: if not svg_path.exists(): print(f"Generating SVG: {svg_path}") save_svg(circuit, svg_path) if not png_path.exists(): print(f"Generating PNG: {png_path}") fig = circuit.plot() if fig: fig.savefig(png_path, transparent=True, bbox_inches='tight', pad_inches=0.1) plt.close(fig) def save_svg(circuit: Circuit, output_path: str): """Generate and save an SVG representation of the circuit.""" if not circuit.coordinates: print(f"No coordinates available for {circuit.name}") return # Extract coordinates lons, lats = zip(*circuit.coordinates) # Normalize coordinates to fit in SVG min_lon, max_lon = min(lons), max(lons) min_lat, max_lat = min(lats), max(lats) # Add some padding lon_padding = (max_lon - min_lon) * 0.05 lat_padding = (max_lat - min_lat) * 0.05 min_lon -= lon_padding max_lon += lon_padding min_lat -= lat_padding max_lat += lat_padding # SVG dimensions svg_width = 800 svg_height = int(svg_width * (max_lat - min_lat) / (max_lon - min_lon)) # Create SVG header svg = [ f'', f'', f' {circuit.name} - {circuit.location}' ] # Create path data path_data = [] for i, (lon, lat) in enumerate(circuit.coordinates): # Convert geo coordinates to SVG coordinates x = svg_width * (lon - min_lon) / (max_lon - min_lon) # Flip y-axis because SVG has origin at top-left y = svg_height * (1 - (lat - min_lat) / (max_lat - min_lat)) if i == 0: path_data.append(f"M{x:.2f},{y:.2f}") else: path_data.append(f"L{x:.2f},{y:.2f}") # Add path to SVG svg.append(f' ') svg.append('') # Write SVG to file os.makedirs(os.path.dirname(output_path), exist_ok=True) with open(output_path, 'w', encoding='utf-8') as f: f.write('\n'.join(svg)) print(f"SVG saved to: {output_path}") def main(): print("Processing circuit files...") process_circuit_files() print("Done!") if __name__ == "__main__": main()