feat(extract): Flamingo Park affin in den Spielpark einpassen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1b0011874e
commit
ad5d0468db
2 files changed
+122
No files matched your search
@@ -7,6 +7,7 @@ Ein Formfehler in der Parkdatei bricht mit ParkError ab, wie bei annotations.tom
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import tomllib
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
@@ -191,3 +192,61 @@ def _is_number(value) -> bool:
|
||||
|
||||
def _is_point(value) -> bool:
|
||||
return isinstance(value, list) and len(value) == 2 and all(_is_number(c) for c in value)
|
||||
|
||||
|
||||
# --- Einpassung --------------------------------------------------------------------------------
|
||||
|
||||
REF_LAT, REF_LON = 25.7840, -80.1375 # Bezugspunkt der lokalen Meter (Spec, Befund)
|
||||
M_PER_DEG_LAT = 111_320.0
|
||||
M_PER_DEG_LON = M_PER_DEG_LAT * math.cos(math.radians(REF_LAT))
|
||||
|
||||
|
||||
def local_metres(lat: float, lon: float) -> tuple[float, float]:
|
||||
"""Breite/Länge -> Meter östlich und nördlich des Bezugspunkts."""
|
||||
return (lon - REF_LON) * M_PER_DEG_LON, (lat - REF_LAT) * M_PER_DEG_LAT
|
||||
|
||||
|
||||
class ParkFit:
|
||||
"""Echter Park -> Spielpark, affin über die Ecken SW, SE und NW (Variante D).
|
||||
|
||||
Bilinear über alle vier Ecken folgte die Anlage der steileren Nordkante im Spiel und verfehlte den
|
||||
Hood Yard um 4,2 m; affin bleibt sie im Nordosten bis zu 6,4 m vor der Nordstraße, der Rasen füllt
|
||||
das ganze Parkpolygon trotzdem.
|
||||
"""
|
||||
|
||||
def __init__(self, config):
|
||||
self.origin = local_metres(*config.real["sw"])
|
||||
se, nw = local_metres(*config.real["se"]), local_metres(*config.real["nw"])
|
||||
self.real_u = (se[0] - self.origin[0], se[1] - self.origin[1])
|
||||
self.real_v = (nw[0] - self.origin[0], nw[1] - self.origin[1])
|
||||
game = config.game
|
||||
self.game_origin = game["sw"]
|
||||
self.game_u = (game["se"][0] - game["sw"][0], game["se"][1] - game["sw"][1])
|
||||
self.game_v = (game["nw"][0] - game["sw"][0], game["nw"][1] - game["sw"][1])
|
||||
self.real_width = math.hypot(*self.real_u)
|
||||
self.game_width = math.hypot(*self.game_u)
|
||||
self.scale_u, self.offset_u = config.scale_u, config.offset_u
|
||||
self.game_quad = Polygon([game["sw"], game["se"], game["ne"], game["nw"]])
|
||||
|
||||
def park_coords(self, x: float, y: float) -> tuple[float, float]:
|
||||
"""Lokale Meter -> (s, t): s von West nach Ost, t von Süd nach Nord, das echte Parkviereck ist [0, 1]²."""
|
||||
dx, dy = x - self.origin[0], y - self.origin[1]
|
||||
(ux, uy), (vx, vy) = self.real_u, self.real_v
|
||||
det = ux * vy - uy * vx
|
||||
return (dx * vy - dy * vx) / det, (ux * dy - uy * dx) / det
|
||||
|
||||
def game_point(self, s: float, t: float) -> tuple[float, float]:
|
||||
u = (self.offset_u + self.scale_u * s * self.real_width) / self.game_width
|
||||
return (self.game_origin[0] + u * self.game_u[0] + t * self.game_v[0],
|
||||
self.game_origin[1] + u * self.game_u[1] + t * self.game_v[1])
|
||||
|
||||
def local_to_game(self, x: float, y: float) -> tuple[float, float]:
|
||||
return self.game_point(*self.park_coords(x, y))
|
||||
|
||||
def to_game(self, lat: float, lon: float) -> tuple[float, float]:
|
||||
return self.local_to_game(*local_metres(lat, lon))
|
||||
|
||||
def real_quad(self) -> Polygon:
|
||||
"""Das echte Parkviereck in lokalen Metern."""
|
||||
(ox, oy), (ux, uy), (vx, vy) = self.origin, self.real_u, self.real_v
|
||||
return Polygon([(ox, oy), (ox + ux, oy + uy), (ox + ux + vx, oy + uy + vy), (ox + vx, oy + vy)])
|
||||
Reference in new issue
Block a user