# API portálu DatováMapa

Strojové rozhraní portálu pro aplikace a AI agenty: katalog dat, data ze všech zdrojů ve sjednoceném tvaru, dotazy na místo (co je v bodě a okolí, souhrn za obec), validátor a registrace pro poskytovatele a MCP server. Vše je veřejné, bez autentizace, s CORS `*`. Formální popis je v [OpenAPI](https://datovamapa.cz/openapi.json).

## Přehled

| Metoda a cesta | Účel |
|---|---|
| `GET /api/catalog` | všichni poskytovatelé a datové sady (JSON) |
| `GET /api/data/{provider}/{dataset}` | data sady jako GeoJSON, přes cache portálu |
| `GET /api/places?query=…` | hledání adresy, ulice nebo obce → souřadnice |
| `GET /api/nearby?point=…` | co je v bodě a okolí, z vybraných sad |
| `GET /api/summary/{provider}/{dataset}` | souhrn sady za celé území nebo za obec |
| `GET /api/validate?manifestUrl=…` | živý test poskytovatele |
| `POST /api/validate` | kontrola manifestu nebo dat bez zveřejnění |
| `POST /api/registration` | registrace poskytovatele (data čekají na schválení) |
| `POST /mcp` | MCP server (Streamable HTTP) |
| `GET /llms.txt`, `/llms-full.txt` | rozcestník a celá dokumentace pro jazykové modely |
| `GET /index.md`, `/zdroje.md`, `/dokumentace/{slug}.md` | stránky portálu v markdownu |
| `GET /schema/0.1/manifest.json`, `/schema/0.1/data.json` | JSON Schema protokolu |
| `GET /schema/0.1/provider-openapi.json` | OpenAPI kontrakt, který implementuje poskytovatel |
| `GET /priklady/{soubor}.json` | platné ukázkové manifesty a odpovědi |

## Katalog

`GET /api/catalog`

```json
{
  "protocol": "0.1",
  "generated": "2026-10-06T08:00:00.000Z",
  "providers": [{ "id": "cgs", "name": "Česká geologická služba", "status": "ok", "datasetCount": 2, "managedBy": "portal" }],
  "datasets": [
    {
      "key": "cgs/radon",
      "providerId": "cgs",
      "providerName": "Česká geologická služba",
      "title": "Radonový index",
      "category": "risks",
      "categoryLabel": "Rizika a omezení",
      "keywords": ["radon"],
      "useCases": ["construction", "housing"],
      "source": { "type": "raster", "tiles": ["https://…"] },
      "presentation": { "type": "raster", "opacity": 0.45 },
      "attribution": "© Česká geologická služba",
      "links": { "data": null, "map": "/?d=cgs/radon" }
    }
  ]
}
```

Katalog obsahuje jen schválená data. Sady poskytovatelů, kteří se zaregistrovali sami a zatím nejsou schválení, v něm nejsou (viz [Registrace](#registrace)).

Datové sady nesou všechna pole z manifestu poskytovatele (`description`, `fields`, `params`, `coverage`…) kromě URL zdroje – GeoJSON data se čtou přes portál (`links.data`), rastrové dlaždice přímo ze zdroje (`source.tiles`). Stejný obsah čitelný pro lidi i modely: [zdroje.md](https://datovamapa.cz/zdroje.md).

## Data

`GET /api/data/{provider}/{dataset}`

| Parametr | Kdy | Popis |
|---|---|---|
| `bbox` | sady s `source.mode = "bbox"` (povinný) | `minLon,minLat,maxLon,maxLat` ve WGS84; portál ho zarovná na dlaždice |
| `zoom` | sady s `source.mode = "bbox"` (povinný) | úroveň přiblížení 0–24 |
| `cursor` | volitelně | kurzor z `dm.next` |
| `{param}` | podle `params` sady | hodnoty ve formátu protokolu (`a,b`, `od,do`); nevyplněné dostanou `default` |

Odpověď je GeoJSON `FeatureCollection`. Údaje z `feature.dm` poskytovatele jsou přesunuté do `properties` s prefixem `__dm_` (`__dm_title`, `__dm_description`, `__dm_url`, `__dm_color`, `__dm_chart`). U sad s `color.type = "trend"` přidá portál `__dm_trend` (`roste`, `stagnuje`, `klesá`) spočítaný z `dm.chart`. Metadata odpovědi:

```json
{ "dm": { "protocol": "0.1", "dataset": "aopk/pamatne-stromy", "generated": "…", "ttl": 86400, "total": 13, "truncated": false, "notice": null } }
```

Hlavička `X-DM-Cache`: `hit` (z cache), `miss` (čerstvě ze zdroje), `stale` (zdroj nedostupný, poslední známá data – viz `dm.notice`).

Chyby mají tvar `{ "error": "zpráva", "code": "kod" }`: `400` neplatný dotaz, `404` neznámá sada, `502` chyba zdroje.

**Sady jen k zobrazení.** U několika sad spravovaných portálem nemá zdroj doloženou licenci k dalšímu šíření. Portál je kreslí ve vlastní mapě, ale data dál nevydává: v katalogu mají `links.data` prázdné a v `links.source` adresu původní služby poskytovatele. Dotaz mimo mapu portálu dostane `303` s hlavičkou `Location` na tuto službu a tělem `{ "error": "…", "code": "display_only", "source": "…" }`. Stejně odpoví nástroj MCP `fetch_data`.

## Místo a okolí

Dotazy pro AI asistenty a aplikace, které odpovídají na otázky o konkrétním místě. Vrací lidsky popsané hodnoty (popisky a jednotky podle `fields` sady) bez geometrií, s atribucí a odkazem na mapu. Návod pro uživatele asistentů: [Data pro AI asistenty](https://datovamapa.cz/dokumentace/ai-asistenti.md).

### Hledání místa

`GET /api/places?query=Thámova 7, Praha` (volitelně `max`, výchozí 5, nejvýš 10)

Hledá v registru adres RÚIAN (© ČÚZK, CC BY 4.0) adresy, ulice, obce, části obcí a další územní prvky. Zadejte jen adresu nebo název; upřesnění jako okres nebo „u Zlína“ služba nezná. `municipality` je obec, ve které místo leží (`name`, `district`, `region` a kód obce ČSÚ `code`) – pro ni umí [souhrn](#souhrn) data za celou obec. U míst mimo města nad 10 000 obyvatel je navíc `near` nejbližší takové město a vzdálenost v km; podle obojího se rozliší stejnojmenná místa.

```json
{
  "query": "Thámova 7, Praha",
  "count": 1,
  "places": [{ "name": "Thámova 221/7, Karlín, 18600 Praha 8", "type": "adresa", "point": [14.452653, 50.091436], "municipality": { "slug": "praha", "code": "554782", "name": "Praha", "district": "Praha", "region": "Hlavní město Praha" } }],
  "attribution": "© ČÚZK, RÚIAN (CC BY 4.0)"
}
```

### Co je v bodě a okolí

`GET /api/nearby`

| Parametr | Popis |
|---|---|
| `point` | `lon,lat` ve WGS84 |
| `place` | místo `point`: adresa nebo název, použije se první nález hledání místa |
| `radius` | poloměr okolí v metrech, výchozí 500, nejvýš 20 000 |
| `datasets` | klíče `provider/dataset` oddělené čárkou, nejvýš 12 |
| `purpose` | místo `datasets`: sady s tímto účelem (`useCases`), které bod pokrývají |
| `search` | místo `datasets`: sady podle shody s textem, např. `rizika pro stavbu` |
| `max` | nejvýš objektů na sadu, výchozí 5, nejvýš 50 |
| `{provider}/{dataset}.{param}` | parametr sady, stejně jako v [odkazu na mapu](#odkaz-na-mapu) |

Jedno z `datasets`, `purpose`, `search` je povinné. Automatický výběr bere jen sady, které na bod umí odpovědět: nejdřív rizika a omezení, podkladové mapy nakonec. Sady nad limit vrátí v `skipped`.

- **GeoJSON sady:** objekty do vzdálenosti `radius` seřazené od nejbližšího. `distance` je v metrech, 0 = bod leží uvnitř plochy (např. obec u kartogramu). `count` je počet objektů v okolí. Sady, které se načítají jen při velkém přiblížení, okolí zmenší (`radius`, `notice`). U celých sad (`mode: full`), které v okolí nic nemají, vrátí aspoň nejbližší objekt (např. měřicí stanici).
- **Rastrové sady:** portál se zeptá zdroje přímo na bod. Dotaz odvodí ze šablony dlaždic: WMS `GetMap` → `GetFeatureInfo`, ArcGIS `MapServer/export` → `identify`, `ImageServer/exportImage` → `identify` s tabulkou tříd rastru. Hodnoty jsou atributy zdroje (názvy polí, jak je zdroj vrací). K výkladu kódů pomůže `description` a `legend`.

| `status` | Význam |
|---|---|
| `ok` | dotaz proběhl; prázdné `hits` = v okolí / v bodě nic není (např. mimo záplavové území) |
| `unavailable` | sada hodnotu v bodě neposkytuje (dlaždice jsou jen obraz) nebo bod leží mimo pokrytí |
| `error` | zdroj selhal (`error`, `code`) |

```json
{
  "place": { "name": "Thámova 221/7, Karlín, 18600 Praha 8", "type": "adresa", "point": [14.452653, 50.091436] },
  "point": [14.452653, 50.091436],
  "radius": 500,
  "generated": "2026-10-06T16:00:00.000Z",
  "results": [
    {
      "dataset": "cgs/radon",
      "title": "Radonový index",
      "provider": "Česká geologická služba",
      "attribution": "© Česká geologická služba",
      "status": "ok",
      "hits": [{ "title": "Radonový index", "distance": 0, "values": [{ "label": "radonovériziko", "value": "přechodný" }, { "label": "stupeňradonovéhoindexu", "value": "2" }] }]
    },
    {
      "dataset": "npu/kulturni-pamatky",
      "title": "Kulturní památky",
      "provider": "Národní památkový ústav",
      "attribution": "Národní památkový ústav",
      "license": "CC BY-SA 4.0",
      "status": "ok",
      "count": 27,
      "radius": 500,
      "hits": [
        {
          "title": "továrna Breitfeld-Daněk",
          "distance": 91,
          "values": [{ "label": "Rejstříkové číslo ÚSKP", "value": "105958" }, { "label": "Stav ochrany", "value": "památkově chráněno" }],
          "url": "https://pamatkovykatalog.cz/x-14624751"
        }
      ]
    }
  ],
  "map": "https://datovamapa.cz/?d=cgs/radon&d=npu/kulturni-pamatky#15/50.09144/14.45265"
}
```

### Souhrn

`GET /api/summary/{provider}/{dataset}?municipality=Ostrava` (ostatní query parametry jsou parametry sady)

Souhrn GeoJSON sady za celé území, nebo s `municipality` jen za obec – kteroukoli obec Česka: `count`, `categories` (počty podle kategorie barvy), `rankings` (nejvyšší a nejnižší hodnoty) a u kartogramu `own` – hodnoty plochy, ve které obec leží. Jsou to stejná čísla, jaká ukazuje stránka sady. Sady načítané jen při velkém přiblížení mají souhrn jen za obec. Rastrové sady souhrn nemají.

`municipality` je název („Ostrava“), název s okresem nebo krajem („Nová Ves (okres Mělník)“), kód obce ČSÚ („535117“, vrací ho [hledání místa](#hledani-mista) v `municipality.code`) nebo adresa jejího přehledu („stredocesky-kraj/nova-ves-melnik“). Samotný název, který nese víc obcí, skončí chybou `ambiguous_place` se seznamem těchto obcí, jejich okresů a kódů.

## Validátor

Nástroj pro poskytovatele a jejich AI agenty. Vrací vždy HTTP 200 a report:

```json
{
  "valid": false,
  "summary": "Manifest není platný: 1 chyb, 2 doporučení.",
  "errors": [{ "path": "datasets[0].source.tiles[0]", "code": "raster_without_template", "message": "Šablona dlaždic neobsahuje {z}/{x}/{y} ani {bbox-epsg-3857}." }],
  "warnings": [{ "path": "datasets[0].keywords", "code": "missing_keywords", "message": "Sada nemá klíčová slova.", "hint": "Doplňte 5–15 slov…" }]
}
```

- `POST /api/validate` s tělem:
  - manifest (objekt s `datasets`) nebo `{ "manifest": … }` – statická kontrola,
  - GeoJSON `FeatureCollection` nebo `{ "data": …, "dataset": … }` – kontrola dat; s `dataset` (objekt sady z manifestu) i soulad s manifestem,
  - `{ "manifestUrl": "https://…" }` – živý test.
- `GET /api/validate?manifestUrl=https://…` – živý test: stáhne manifest, zkontroluje ho a na každou sadu pošle testovací dotaz jako portál (u `bbox` na střed `coverage.bbox`, zoom 12 nebo `coverage.minZoom`; u rastru jednu dlaždici). Report má navíc `provider` a `datasets[]` s `request`, `durationMs`, `featureCount`, `errors`, `warnings`.

## Registrace

`POST /api/registration` s tělem `{ "manifestUrl": "https://…" }`. Portál manifest zkontroluje živým testem (jako validátor). Když projde, zaregistruje poskytovatele mezi neschválená data:

- Data jsou dostupná přes `/api/data/…` i na stránkách sad `/mapa/{provider}/{dataset}`. Stránka nejdřív ukáže varování, že data nahrála třetí strana a provozovatel je neschválil. Data do mapy načte až po potvrzení. Stránka má `noindex`.
- Do katalogu, vyhledávání, `llms.txt`, MCP nástroje `catalog` a na stránky za obce se data dostanou až po schválení provozovatelem.
- Schválení platí pro otisk (SHA-256) obsahu manifestu. Po změně manifestu je poskytovatel zase neschválený, dokud ho provozovatel neschválí znovu.
- Přehled neschválených poskytovatelů: [/neschvalena-data](https://datovamapa.cz/neschvalena-data).

```json
{
  "registered": true,
  "status": "pending_review",
  "message": "Registrováno. …",
  "provider": { "id": "obec-priklad", "name": "Obec Příklad" },
  "links": { "review": "/neschvalena-data#obec-priklad", "datasets": ["/mapa/obec-priklad/lavicky"] },
  "check": { "summary": "Poskytovatel je platný bez výhrad.", "warnings": [] }
}
```

| HTTP | `code` | Význam |
|---|---|---|
| 201 | – | registrováno, čeká na schválení |
| 200 | – | už registrováno; `status` je `pending_review` nebo `approved` |
| 400 | `invalid_input`, `invalid_url` | chybí nebo je neplatná `manifestUrl` |
| 403 | `rejected` | registraci provozovatel zamítl |
| 409 | `id_taken`, `url_registered` | `provider.id` už v portálu je / URL je registrovaná pod jiným id |
| 422 | `manifest_rejected` | živý test neprošel; report je v `check` |
| 429 | `too_many_requests`, `queue_full` | víc než 5 pokusů za minutu z jedné adresy / ke schválení čeká přes 100 poskytovatelů |
| 503 | `registration_unavailable` | registrace teď nefunguje |

## MCP server

Endpoint `https://datovamapa.cz/mcp`, transport Streamable HTTP (bezstavový, odpovědi JSON), bez autentizace.

| Nástroj | Vstup | Výstup |
|---|---|---|
| `documentation` | `topic`: `ai-agents`, `ai-assistants`, `protocol`, `providers`, `api`, `about` | markdown dokumentu |
| `schema` | `type`: `manifest`, `data`, `provider-openapi` | JSON Schema / OpenAPI |
| `example` | `name`: `manifest-minimal`, `manifest-complete`, `data-points`, `data-polygons` | platný ukázkový JSON |
| `validate_manifest` | `manifest` (objekt) nebo `manifestUrl` (živý test) | report validátoru |
| `validate_data` | `data`, volitelně `dataset` | report validátoru |
| `register_provider` | `manifestUrl` | výsledek [registrace](#registrace) |
| `catalog` | volitelně `search` (volný text), `point` (`[lon, lat]`) | datové sady v portálu, seřazené podle shody; `pointQuery` = sada odpoví `nearby` |
| `find_place` | `query`, `maxResults` | jako [hledání místa](#hledani-mista) |
| `nearby` | `point` nebo `place`; `datasets`, `purpose` nebo `search`; `radius`, `maxFeatures`, `params` (`{ "provider/dataset": { "param": "hodnota" } }`) | jako [co je v bodě a okolí](#co-je-v-bode-a-okoli) |
| `summary` | `dataset`, volitelně `municipality`, `params` | jako [souhrn](#souhrn) |
| `fetch_data` | `dataset` (`provider/dataset`), `bbox` nebo `municipality`, `zoom`, `params`, `maxFeatures` | GeoJSON (zkrácený na `maxFeatures`) |

Připojení:

```bash
claude mcp add --transport http datovamapa https://datovamapa.cz/mcp
```

```json
{ "mcpServers": { "datovamapa": { "type": "http", "url": "https://datovamapa.cz/mcp" } } }
```

## Odkaz na mapu

`https://datovamapa.cz/?d={provider}/{dataset}&d=…&{provider}/{dataset}.{param}={hodnota}#{zoom}/{lat}/{lon}`

Každá sada má i vlastní stránku s mapou a popisem: `https://datovamapa.cz/mapa/{provider}/{dataset}`, celostátní GeoJSON sady také za obce nad 10 000 obyvatel: `https://datovamapa.cz/mapa/{provider}/{dataset}/{obec}` (např. [/mapa/chmu/kvalita-ovzdusi/ostrava](https://datovamapa.cz/mapa/chmu/kvalita-ovzdusi/ostrava)). Odkaz na stránku sady je v katalogu v `links.map`. Na stránce sady (i sestavené mapy `/mapy/{slug}`) se do `?d=…` zapisuje jen rozdíl proti výchozímu výběru: bez `d` platí sady stránky, `d` vyjmenuje celý výběr (`?d=` = nic zapnuto) a parametr se uvádí, jen když se liší od výchozího.

Příklad – dopravní nehody v Brně souhrnně za roky 2019–2023: [/?d=brno/dopravni-nehody&brno/dopravni-nehody.obdobi=2019,2023#13/49.195/16.608](https://datovamapa.cz/?d=brno/dopravni-nehody&brno/dopravni-nehody.obdobi=2019,2023#13/49.195/16.608)

## Kódy nálezů

Stabilní kódy chyb (`errors`) a doporučení (`warnings`) validátoru a proxy.

### Manifest

| Kód | Úroveň | Význam |
|---|---|---|
| `schema_*` | chyba | porušení JSON Schema (`schema_invalid_type`, `schema_too_small`, `schema_invalid_format`, `schema_invalid_value`, `schema_custom`…), `path` ukazuje místo |
| `unsupported_version` | chyba | jiná hlavní verze protokolu |
| `raster_without_template` | chyba | URL dlaždic bez `{z}/{x}/{y}` i `{bbox-epsg-3857}` |
| `geojson_with_raster_presentation` | chyba | GeoJSON zdroj s `presentation.type: "raster"` |
| `param_without_options` | chyba | `enum` / `multi-enum` bez `options` |
| `invalid_default` | chyba | `default` parametru neodpovídá typu nebo možnostem |
| `time_param_missing` | chyba | `temporal.param` odkazuje na parametr, který není v `params` |
| `time_param_type` | chyba | časový parametr není typu `range` ani `date-range` |
| `time_param_no_range` | doporučení | časový parametr typu `range` nemá `min` a `max` |
| `unknown_presentation_type` | doporučení | portál typ nezná, kreslí `auto` |
| `raster_without_raster_presentation` | doporučení | rastr se vždy kreslí jako `raster` |
| `missing_description`, `missing_keywords`, `missing_use_cases` | doporučení | sadu hůř najdou lidé i asistent |
| `missing_category`, `unknown_category` | doporučení | sada skončí v „Ostatní“ |
| `missing_field` | doporučení | detail objektu ukáže surové názvy vlastností |
| `field_not_described`, `field_not_number` | doporučení | prezentace odkazuje na vlastnost mimo `fields` / s nečíselným typem |
| `bbox_unbounded` | doporučení | sada `bbox` bez `coverage.minZoom` a `maxFeatures` |
| `missing_coverage` | doporučení | sada `bbox` bez `coverage.bbox` |
| `missing_license`, `missing_website` | doporučení | chybí licence / web poskytovatele |

### Data

| Kód | Úroveň | Význam |
|---|---|---|
| `coordinates_out_of_range` | chyba | souřadnice mimo WGS84 (typicky nepřevedené S-JTSK) |
| `unclosed_polygon` | chyba | prstenec polygonu není uzavřený |
| `too_many_features` | doporučení | víc objektů než `limit` / 5 000 |
| `missing_time_series` | doporučení | sada má `temporal.param`, odpověď neposílá `dm.timeline` ani `dm.chart` |
| `missing_trend_chart` | doporučení | sada barví podle číselné hodnoty a období volí parametrem, objekty ale neposílají `dm.chart` |
| `trend_without_chart` | doporučení | sada se barví podle trendu, některé objekty nemají `dm.chart` (zůstanou šedé) |
| `swapped_coordinates` | doporučení | souřadnice zřejmě v pořadí `[lat, lon]` |
| `outside_coverage` | doporučení | většina objektů mimo `coverage.bbox` |
| `geometry_presentation_mismatch` | doporučení | např. plochy u `circle` |
| `field_missing_in_data`, `missing_presentation_property` | doporučení | vlastnost z manifestu v datech chybí |
| `missing_id` | doporučení | objekty bez `id` |
| `empty_geometry` | doporučení | objekty s `geometry: null` se přeskočí |

### Živý test a proxy

| Kód | Význam |
|---|---|
| `invalid_url`, `https_required` | neplatná URL / zdroj není na HTTPS |
| `source_unreachable`, `timeout`, `http_error` | zdroj neodpovídá, nestihl 10 s, vrátil HTTP chybu |
| `invalid_json`, `empty_body`, `response_too_large` | odpověď není JSON / je prázdná / má víc než 15 MB |
| `invalid_manifest`, `id_mismatch` | manifest registrovaného poskytovatele neprošel / má jiné `provider.id` |
| `registration_unavailable`, `too_many_requests`, `queue_full`, `manifest_rejected`, `id_taken`, `url_registered`, `rejected` | chyby [registrace](#registrace) |
| `data_schema_mismatch` | data od zdroje neodpovídají schématu |
| `empty_response`, `slow_response` | testovací dotaz vrátil 0 objektů / trval přes 3 s |
| `tile_http_error`, `tile_not_image`, `tile_unreachable` | testovací rastrová dlaždice selhala |
| `source_error` | jiná chyba při komunikaci se zdrojem (podrobnosti v `message`) |
| `invalid_query`, `unknown_dataset`, `raster_dataset`, `invalid_input`, `internal_error` | chyby dotazu na portál |
| `display_only` | sadu portál jen zobrazuje, data poskytuje původní služba (`source`) |
| `missing_place`, `unknown_place` | dotaz na místo bez bodu i místa / místo (adresa, obec) se nepodařilo najít |
| `geocoding_unavailable` | hledání adres a míst v dané zemi portál zatím nemá (HTTP 501) |
| `ambiguous_place` | název obce nese víc obcí – zpráva je vyjmenuje s okresem a kódem |
| `missing_dataset_selection` | dotaz na okolí bez `datasets`, `purpose` i `search` |
| `summary_requires_place` | sada se dá načíst jen po přiblížení, souhrn za celé území nemá – zadejte obec |
| `unknown_schema`, `unknown_example` | neexistující schéma / příklad (odpověď obsahuje seznam `available`) |
