Formule jedna web
Work in Progress
Web Development

Formule jedna web

F1UXJS

Pokus o fanouškovský web na téma formule jedna, s využitím volně dostupného kódu pro live tracking závodů

// Jak to funguje

Dva zdroje dat, dva různé účely

Jolpica-F1 mi dává klasiku: kalendář, standings a výsledky. OpenF1 používám tam, kde je potřeba telemetry, třeba GPS body tratě nebo průběžné pozice během závodu. Dva zdroje, dvě různé práce.

Nejdřív potřebuju říct, odkud data vůbec jsou. Každý zdroj odpovídá na jinou otázku a putuje jinou cestou.

Jolpica→Race / standings data
OpenF1→Telemetry→Track SVG / position timeline

Klasická data jdou rovnou do přehledů. Telemetry musí projít převodem na tvary, které frontend umí nakreslit, a právě tenhle převod je nejzajímavější část projektu.

// Kousek kódu

Proč vlastní klient pro Jolpica

KÓD 01 · API CLIENTbackend/jolpica_client.py
def _get(path: str) -> dict:
    if not path.endswith("/"):
        path += "/"
    resp = requests.get(
        f"{BASE_URL}/{path}",
        headers={"User-Agent": USER_AGENT},
        timeout=REQUEST_TIMEOUT,
    )
    resp.raise_for_status()
    return resp.json()["MRData"]


def get_schedule(season: int) -> list:
    """List of races for a season: round, name, date/time, circuit info."""
    data = _get(f"{season}")
    races = data.get("RaceTable", {}).get("Races", [])
Co to děláCentrálně řeší URL, User-Agent, timeout a převod zdrojových dat do jednodušších struktur pro aplikaci.
Proč na tom záležíJe důležité nenechat HTTP detaily rozlézt do každého endpointu. Klient také normalizuje odpověď tak, aby frontend nepotřeboval znát původní Ergast/Jolpica JSON strukturu.
// Kousek kódu

Trať vzniká z telemetry

KÓD 02 · GPS → SVGbackend/circuit_service.py
def _decimate(points: list, max_points: int = MAX_POINTS) -> list:
    if len(points) <= max_points:
        return points
    step = len(points) / max_points
    return [points[int(i * step)] for i in range(max_points)]


def _points_to_path(points: list) -> dict:
    xs = [p["x"] for p in points]
    ys = [p["y"] for p in points]
    min_x, max_x = min(xs), max(xs)
    min_y, max_y = min(ys), max(ys)
    span_x = max(max_x - min_x, 1)
    span_y = max(max_y - min_y, 1)

    avail_w = VIEWBOX_W - 2 * PADDING
    avail_h = VIEWBOX_H - 2 * PADDING
    scale = min(avail_w / span_x, avail_h / span_y)
Co to děláRedukuje množství GPS bodů a převádí souřadnice do pevného SVG viewBoxu se zachováním měřítka a převrácením osy Y.
Proč na tom záležíOpenF1 může vrátit velké množství bodů. Decimace udrží SVG malé a čitelné, zatímco normalizace dovolí vykreslit různé tratě ve stejném UI komponentu.
// Kousek kódu

Live Race Tracker pracuje s reálnou časovou osou

KÓD 03 · POSITION TIMELINEbackend/race_position_service.py
def build_position_timeline_sync(circuit_key: int, year: int) -> Optional[dict]:
    session = _find_race_session(circuit_key, year)
    if not session:
        return None

    raw = _get("position", session_key=session["session_key"])
    raw = [p for p in raw if p.get("position") is not None and p.get("driver_number") is not None]
    if not raw:
        return None

    raw.sort(key=lambda p: p["date"])
    start = datetime.fromisoformat(raw[0]["date"])

    events = [
        {
            "t": round((datetime.fromisoformat(p["date"]) - start).total_seconds(), 1),
            "driver_number": p["driver_number"],
            "position": p["position"],
        }
        for p in raw
    ]
Co to děláNačte position feed závodu, seřadí události podle času a převede timestampy na relativní čas od začátku závodu.
Proč na tom záležíFrontend pak může replayovat skutečné pořadí místo simulace. To je přesně rozdíl mezi vizuální demo animací a vizualizací dat z reálného závodu.
// Jak to funguje

Cache není jen optimalizace

Server má obecnou `_cached` vrstvu pro data z Jolpiky. Kalendář se klidně drží dlouho, standings kratší dobu a finální pořadí celý týden. Když živé stažení selže, server sáhne po starší cache. Prostě záchranná síť pro případ, že externí služba vypadne.

// Jak to funguje

SVG místo obrázků

Tratě jsou SVG path, takže se škálují bez ztráty kvality a nemusím řešit obrázky. Backend umí použít i předpřipravené layouty z repozitáře, takže u známých závodů nemusí pokaždé sahat ven.

// Jak to funguje

Co bylo technicky zajímavé

Nejzajímavější je převod cizích dat na něco, co UI opravdu chce. Není to jen fetch: musíš rozhodnout, co se cachuje, jak se to normalizuje, co se stane při výpadku API a jak z hromady telemetry vyrobit jednu malou čistou trať.

// Jak to funguje

Rozšíření

Architektura je připravená na další vizualizace závodů, standings a vlastnosti tratí. Největší prostor je v dalších datových vrstvách, ne v přidávání náhodných UI komponent.

// Pod kapotou

Z čeho je projekt postavený

Žádná technologie tu není jen pro parádu. Tady je rychlý přehled, kdo za co odpovídá.

FRONTEND

React + CRACO

BACKEND API

FastAPI

DATA ZÁVODŮ

Jolpica-F1

TELEMETRY

OpenF1

CACHE

MongoDB cache

VIZUALIZACE

SVG / charts

// Shrnutí

Co je na projektu podstatné?

Nejde mi jen o to, jak to vypadá. Baví mě hlavně to, co je pod tím: kudy data tečou, co se validuje, co může selhat a proč jsem to udělala právě takhle.

Napiš mi