Oficiální meteo výstrahy správně: CAP, atribuce a rozšiřitelnost

Vládní výstrahy jsou zadarmo a veřejné — „prostě je zobrazit" je ale past: různé zdroje, různé severity škály, povinná atribuce, expirace, lokální časové zóny.

Oficiální meteo výstrahy správně: CAP, atribuce a rozšiřitelnost

Proč „jen zobrazit výstrahy“ nestačí

Oficiální meteorologické výstrahy jsou z logiky věci veřejné a bezplatné. Státní instituce je publikují s cílem chránit životy a majetek, což z nich dělá ideální datový zdroj pro jakoukoli meteorologickou platformu. Na první pohled se zdá, že implementace je triviální záležitost: stačí najít API, stáhnout JSON nebo XML, uložit ho do databáze a vykreslit uživateli na mapu nebo do detailu předpovědi.

Tento naivní přístup je však spolehlivou cestou k nepřehlednému, chybovému a dlouhodobě neudržitelnému kódu. Jakmile se pokusíte integrovat oficiální výstrahy počasí do aplikace, která má ambici fungovat globálně nebo alespoň na úrovni více států, narazíte na sérii architektonických a UX překážek.

Prvním problémem je nejednotnost formátů a klasifikací. Každá národní meteorologická služba používá vlastní metodiku pro určování závažnosti jevů. Co je pro jednoho „střední nebezpečí“, to jiný označuje žlutou barvou nebo stupněm 2 z 5. Bez jednotné normalizační vrstvy nelze v uživatelském rozhraní nabídnout konzistentní podobu.

Druhým úskalím je správa životního cyklu výstrah. Výstrahy nejsou statická data. Jsou průběžně aktualizovány, doplňovány a rušeny, nebo jim prostě vyprší platnost. Pokud vaše aplikace nedokáže spolehlivě detekovat, že platnost výstrahy již skončila, nebo že nová zpráva plně nahrazuje tu předchozí, riskujete, že uživatele zahltíte neaktuálními informacemi, což v kritických situacích podkopává důvěru v celou platformu.

K tomu se přidávají přísné licenční podmínky vyžadující přesnou atribuci zdroje, nutnost správné práce s časovými pásmy v místě dopadu události a potřeba překladu odborných textů do jazyka uživatele. To vše ukazuje, že integrace výstrah vyžaduje robustní softwarovou architekturu, nikoli pouze jednoduchý skript pro stahování dat.

CAP: společný jazyk výstrah

Abychom nemuseli pro každou zemi vyvíjet unikátní parser, existuje mezinárodní standard CAP (Common Alerting Protocol). Jedná se o XML formát (případně jeho JSON variantu), který definuje univerzální strukturu pro šíření nouzových zpráv a varování před nebezpečnými jevy.

Standard CAP rozděluje výstrahu do několika klíčových segmentů:

  • Event (Událost): Konkrétní typ nebezpečí (např. silný vítr, povodeň, extrémní horko).
  • Severity (Závažnost): Míra dopadu na životy a majetek (Extreme, Severe, Moderate, Minor, Unknown).
  • Urgency (Naléhavost): Časový rámec, ve kterém je nutné jednat (Immediate, Expected, Future, Past, Unknown).
  • Certainty (Jistota): Pravděpodobnost, že jev skutečně nastane (Observed, Likely, Possible, Unlikely, Unknown).
  • Description a Instruction (Popis a instrukce): Textové informace popisující povahu hrozby a doporučené chování pro minimalizaci rizik.
  • Area (Oblast): Geografická specifikace zasaženého území, nejčastěji definovaná polygonem nebo sadou geokódů (např. FIPS kódy v USA).
  • Validity (Platnost): Časová okna definující začátek (onset), okamžik účinnosti (effective) a konec platnosti (expires).

Ačkoli CAP představuje obrovský krok vpřed k unifikaci, v praxi se stále setkáte s drobnými odchylkami. Různé meteorologické služby implementují standard s drobnými nuancemi, využívají vlastní XML jmenné prostory nebo specifickým způsobem interpretují kombinace polí severity, urgency a certainty. Naše aplikace proto musí s CAP pracovat jako s výchozím bodem, nad kterým staví vlastní validační a normalizační logiku.

První zdroj: NWS a geografický gate

Jako první produkční zdroj pro oficiální výstrahy počasí v aplikaci byla zvolena americká služba NWS (National Weather Service). NWS poskytuje robustní a bezplatné API, které pokrývá území Spojených států amerických, Aljašku, Havaj a Portoriko.

Volání externího API pro každý uživatelský dotaz na předpověď je však neefektivní. Pokud uživatel vyhledává počasí v Praze, je zcela zbytečné dotazovat servery NWS v USA. Z tohoto důvodu je před samotné volání adaptéru předsazena takzvaná geografická brána.

Každý adaptér pro konkrétní zdroj výstrah implementuje metodu, která na základě zeměpisných souřadnic (šířky a délky) rozhodne, zda má smysl daný zdroj vůbec dotazovat.

interface AlertSource {
  readonly id: string;
  readonly attribution: string;
  covers(latitude: number, longitude: number): boolean;
  fetchAlerts(latitude: number, longitude: number): Promise<RawAlert[]>;
}

export class NwsAlertSource implements AlertSource {
  readonly id = 'nws';
  readonly attribution = 'National Weather Service (NWS)';

  covers(latitude: number, longitude: number): boolean {
    // Geografický gate omezující dotazy pouze na území USA a přidružených oblastí
    const isWithinUS = latitude >= 24.396308 && latitude <= 49.384358
                    && longitude >= -125.00165 && longitude <= -66.93457;
    const isAlaska = latitude >= 51.214183 && latitude <= 71.3895
                  && longitude >= -179.148909 && longitude <= -129.9795;
    const isHawaii = latitude >= 18.910361 && latitude <= 28.402123
                  && longitude >= -178.334698 && longitude <= -154.806773;
    const isPuertoRico = latitude >= 17.926822 && latitude <= 18.520556
                      && longitude >= -67.271444 && longitude <= -65.244111;

    return isWithinUS || isAlaska || isHawaii || isPuertoRico;
  }

  async fetchAlerts(latitude: number, longitude: number): Promise<RawAlert[]> {
    // Volání API NWS a stažení dat pro dané souřadnice
  }
}

Díky tomuto mechanismu jádro aplikace okamžitě ví, které adaptéry má pro danou lokalitu aktivovat. Pokud se uživatel dotáže na lokalitu mimo pokrytí NWS, adaptér je vyřazen z řetězce volání, což šetří síťovou kapacitu i výpočetní čas.

Normalizace: jedna škála závažnosti pro všechny

Jakmile získáme surová data z jednotlivých adaptérů, nastává klíčová fáze: normalizace. Cílem je převést specifické datové struktury různých poskytovatelů na jednotný interní model, se kterým dokáže pracovat zbytek aplikace a frontend.

Normalizační proces řeší několik zásadních úkolů:

  1. Sjednocení škály závažnosti: CAP rozlišuje pět úrovní závažnosti, ale různé služby je interpretují odlišně. V interním modelu zůstává pětice úrovní CAP, jen znormalizovaná na malá písmena a seřazená podle závažnosti — mapování ze své vlastní škály si nese každý adaptér sám.
  2. Deduplikace výstrah: Stejná výstraha může být publikována ve více zprávách (např. původní varování, aktualizace popisu, upřesnění polygonu). Normalizační vrstva slučuje zprávy podle jednoho stabilního identifikátoru a zahazuje zrušené (msgType: Cancel) i prošlé, takže se jedna událost v seznamu objeví jen jednou.
  3. Filtrace neaktivních výstrah: Výstrahy, jejichž časový údaj expires již nastal vzhledem k aktuálnímu času na serveru, jsou automaticky zahozeny.
  4. Třídění podle priorit: Pokud pro jedno místo platí více výstrah současně (např. silný vítr a zároveň povodňová bdělost), jsou seřazeny sestupně podle závažnosti, aby ty nejkritičtější byly uživateli prezentovány na prvním místě.
export type AlertSeverity =
  | 'extreme'
  | 'severe'
  | 'moderate'
  | 'minor'
  | 'unknown';

export interface NormalizedAlert {
  id: string;
  sourceId: string;
  event: string;
  severity: AlertSeverity;
  headline: string;
  description: string;
  instruction?: string;
  startsAt: Date;
  expiresAt: Date;
  attribution: string;
}

Tato striktní typovost zaručuje, že zbytek aplikačního kódu nemusí řešit, odkud výstraha pochází. Všechny výstrahy bez ohledu na původní formát splňují stejný kontrakt.

Port-based architektura: přidat zdroj bez sahání do jádra

Aby byla platforma dlouhodobě udržitelná, je navržena podle principů port-based architektury (známé také jako Hexagonal Architecture). Jádro aplikace definuje rozhraní (porty), přes která komunikuje s vnějším světem. Konkrétní implementace těchto rozhraní (adaptéry) pak sídlí na okraji systému.

Tento přístup je detailněji popsán v článku o tom, jak je platforma poskládaná. V kontextu výstrah je portem právě rozhraní AlertSource.

┌────────────────────────────────────────────────────────┐
│                     JÁDRO SLUŽBY                       │
│  (AlertService - NestJS, cache v procesu)              │
│                                                        │
│             Definuje port: AlertSource                 │
└──────────────────────────┬─────────────────────────────┘
                           │
         ┌─────────────────┴─────────────────┐
         ▼                                   ▼
┌─────────────────┐                 ┌─────────────────┐
│  NwsAdapter     │                 │   ChmiAdapter   │
│  (USA)          │                 │     (Česko)     │
│                 │                 │                 │
│ Implementuje    │                 │ Implementuje    │
│ AlertSource     │                 │ AlertSource     │
└─────────────────┘                 └─────────────────┘

Přesně takto k NWS přibylo dalších šest zdrojů — český ČHMÚ, německý DWD, irský Met Éireann, indický IMD, norský MET Norway a kanadský MSC — aniž by se sáhlo na jediný řádek v jádru služby AlertService. U každého z nich vznikl pouze nový adaptér implementující rozhraní AlertSource, zaregistroval se do DI kontejneru a systém začal jeho výstrahy automaticky stahovat, normalizovat a zobrazovat.

Tato volná vazba výrazně usnadňuje testování. Při psaní unit a integračních testů pro jádro služby nemusíme reálně volat po síti na servery NWS. Stačí podstrčit mockovací adaptér, který vrací předem definovaná testovací data.

Povinná atribuce a lokální časové zóny

Při zobrazování oficiálních výstrah nelze opomenout legislativní a UX požadavky. Většina poskytovatelů dat (včetně těch veřejných) vyžaduje v rámci licencí (např. CC BY) jasné uvedení zdroje informací.

V uživatelském rozhraní platformy je proto u každé zobrazené výstrahy striktně vyžadována atribuce. Uživatel musí na první pohled vidět, která autorita výstrahu vydala. V databázi i v odpovědích API je proto pole attribution povinnou součástí každého normalizovaného záznamu.

Druhým, technicky mnohem náročnějším oříškem je správná interpretace časových údajů. Představte si uživatele, který sedí v Praze (středoevropský čas, CET) a plánuje cestu do Miami na Floridě (východní čas, EST). Pokud si v aplikaci zobrazí detail počasí pro Miami a uvidí varování před hurikánem s platností „do 22:00“, v jakém časovém pásmu tento údaj je?

Zobrazení v čase uživatele (CET) je matoucí, protože uživatel se snaží pochopit situaci na místě dopadu. Zobrazení v UTC je pro běžného člověka nepoužitelné. Správným řešením je zobrazit čas v lokální časové zóně místa, pro které je výstraha vydána.

Při zpracování souřadnic dotazované lokality proto aplikace nejprve zjistí příslušné časové pásmo podle IANA (např. America/New_York). Časové údaje o platnosti výstrahy jsou sice interně ukládány v UTC, ale při formátování pro uživatelské rozhraní jsou transformovány do cílového časového pásma dané lokality. Uživatel v Praze tak vidí, že výstraha v Miami platí do 16:00 místního času (EST), což přesně odpovídá reálné situaci na místě.

Když je výstraha v cizím jazyce

Oficiální výstrahy vydávají národní autority primárně v úředním jazyce dané země. NWS publikuje zprávy v angličtině, norský MET Norway v norštině a ČHMÚ v češtině. Pro globální platformu to představuje vážný UX problém. Pokud uživatel z Česka cestuje po Norsku a obdrží kritickou výstrahu před silným větrem, potřebuje porozumět instrukcím okamžitě, bez nutnosti kopírovat texty do externích překladačů.

Pro vyřešení tohoto problému byl do platformy integrován systém překladů na vyžádání. Vzhledem k tomu, že překlady generované velkými jazykovými modely jsou zpoplatněné, celý proces je přísně optimalizovaný:

  1. On-demand spuštění: Překlad textu výstrahy (titulek, popis a instrukce) se nespouští automaticky při stažení výstrahy na server. Aktivuje se až ve chvíli, kdy si konkrétní uživatel vyžádá detail výstrahy v jazyce, který se neshoduje s originálním jazykem zprávy.
  2. Cachování výsledků: Jakmile je překlad pro danou kombinaci ID výstrahy a cílového jazyka jednou vygenerován, uloží se do databáze. Všichni další uživatelé, kteří si zobrazí stejnou výstrahu ve stejném jazyce, již dostávají nacachovaný výsledek. Protože stejnou výstrahu může v zasažené oblasti otevřít mnoho lidí, je cache navržená tak, aby se každý překlad zaplatil právě jednou.
  3. Integrace Gemini: Pro samotný překlad se využívá API modelu Gemini. Model dostává jako kontext informaci, že se jedná o meteorologické varování, což zajišťuje přesné zachování odborné terminologie a tónu instrukcí.
  4. Graceful degradation: Pokud překladové API selže (např. z důvodu překročení limitů počtu dotazů nebo výpadku služby), aplikace nesmí spadnout. V takovém případě tiše degraduje a uživateli zobrazí původní text v originálním jazyce s informací, že překlad dočasně není dostupný. Pokud se během procesu překladu vyskytne neočekávaná chyba, interní systém pro správu kvót zajistí, že se neúspěšný pokus nezapočítá do limitů spotřeby.

Je důležité zdůraznit, že tento článek popisuje výhradně pasivní vrstvu: příjem, normalizaci, překlad a zobrazení výstrah vydaných státními autoritami. Uživatelsky definovaná pravidla nad předpovědí (např. „upozorni mě, pokud rychlost větru v mé lokalitě překročí 15 m/s“) jsou samostatný subsystém s vlastním vyhodnocováním na pozadí — věnuje se mu článek vlastní výstrahy na počasí.


Robustní integrace oficiálních výstrah počasí v aplikaci ukazuje, že i zdánlivě jednoduchá integrace veřejných dat vyžaduje promyšlenou architekturu. Použití standardu CAP, geografických bran, port-based návrhu a efektivního cachování překladů umožňuje platformě škálovat a snadno se rozšiřovat o další globální zdroje. Pokud vás zajímá, jak jsou tyto principy aplikovány v praxi, navštivte stránku popisující celý projekt Weather InTouch.

Související články

Kontaktní informace

Klidně se mi ozvěte ohledně nového projektu, konzultace nebo dlouhodobější spolupráce. Rád proberu smysluplné produktové zadání i zajímavé frontendové příležitosti.

Napište mi

© 2026 Martin Hubálek. Všechna práva vyhrazena.

Martin Hubálek IČO: 08827303 Sídlo: Dolní Štěpanice 29, 514 01 Benecko, Česká republika Zapsán v živnostenském rejstříku Nejsem plátce DPH