Anleitung für KI-Agenten: Daten an die Datenkarte anbinden
Dieses Dokument richtet sich an KI-Agenten, die Daten für das Portal Datenkarte aufbereiten. Datenanbieter kann jede und jeder sein, die oder der Daten mit Ortsbezug hat. Es beschreibt Ziel, Vorgehen, Regeln und Abnahmekriterien. Ein Mensch, der den Agenten steuert, findet eine leichter verständliche Fassung im Leitfaden für Datenanbieter.
Ziel
Über HTTPS veröffentlichen:
- das Manifest – JSON gemäß Manifest-Schema,
- für jeden Datensatz eine Datenquelle – eine GeoJSON-Datei oder einen Endpunkt gemäß Datenschema und OpenAPI-Vertrag für Anbieter, gegebenenfalls bestehende Rasterkacheln / WMS,
und zwar so, dass der Validator "valid": true und möglichst wenige Warnungen zurückgibt.
Was Sie lesen sollten
| Quelle | Wozu |
|---|---|
| llms.txt | Übersicht über die gesamte Dokumentation |
| Protokoll 0.1 | normative Spezifikation – im Zweifel gilt sie |
| JSON Schema des Manifests, der Daten | genaue Typen, Grenzwerte und Feldbeschreibungen |
| OpenAPI für Anbieter | Vertrag des Endpunkts, daraus lässt sich ein Server generieren |
| manifest-complete.json, manifest-minimal.json | gültige Vorlagen |
| data-points.json, data-polygons.json | gültige Vorlagen für Antworten |
| Katalog | was das Portal bereits enthält – vermeiden Sie Duplikate, lassen Sie sich von den Schlüsselwörtern inspirieren |
Wenn Sie MCP nutzen können, verbinden Sie sich mit dem Server https://datenkarte.at/mcp (Streamable HTTP, ohne Authentifizierung). Die Werkzeuge documentation, schema, example, validate_manifest, validate_data und catalog decken alles in dieser Anleitung ab. Siehe MCP-Server.
Vorgehen
1. Bestandsaufnahme der Daten
Ermitteln Sie, welche räumlichen Daten der Anbieter hat: Datenbanken (PostGIS, MS SQL), GIS-Dateien (SHP, GPKG, KML, CSV mit Koordinaten), bestehende APIs, WMS/WMTS-Dienste. Klären Sie für jede Quelle:
- den Geometrietyp (Punkte / Linien / Flächen),
- das Koordinatenreferenzsystem (in Österreich häufig MGI / Austria Lambert, EPSG:31287 oder Gauß-Krüger M28/M31/M34, EPSG:31257/31258/31259 – muss umgerechnet werden),
- die Anzahl der Objekte und wie oft sie sich ändern,
- die Lizenz und wer sie freigeben darf,
- ob sie personenbezogene Daten enthält (Namen, Sozialversicherungsnummern, Adressen natürlicher Personen, Kfz-Kennzeichen …).
2. Entwurf der Datensätze
- Ein Datensatz = eine Art von Objekten mit einer Darstellungsweise. Müllcontainer und eine Lärmkarte gehören in zwei Datensätze.
iddes Datensatzes und des Anbieters: Kleinbuchstaben ohne Umlaute und Sonderzeichen, Ziffern, Bindestriche (muellcontainer). Ändern Sie sie nach der Veröffentlichung nicht – sie ist Teil von Links.- Wahl der Quelle:
| Situation | source |
|---|---|
| bis ca. 2 000 Objekte, Änderungen höchstens täglich | statische Datei, "type": "geojson", "mode": "full" |
| mehr Objekte oder Live-Daten | Endpunkt, "mode": "bbox", setzen Sie coverage.bbox und bei großen Datensätzen coverage.minZoom |
| es gibt bereits WMS / XYZ-Kacheln | "type": "raster" mit der Vorlage {bbox-epsg-3857} oder {z}/{x}/{y}; ergänzen Sie presentation.legend (Farben und Beschriftungen aus der Legende des Dienstes, z. B. ArcGIS …/MapServer/legend?f=json oder WMS GetLegendGraphic) |
3. Umwandlung der Daten
- Koordinaten in WGS84 (EPSG:4326), Reihenfolge
[lon, lat]. Für Österreich ist das etwa[14, 47.5], nicht[47.5, 14]. - Runden Sie auf 6 Dezimalstellen (≈ 10 cm), alles Weitere vergrößert nur die Antwort.
- Polygone müssen geschlossene Ringe haben (erster Punkt = letzter Punkt).
- Jedes Objekt sollte eine stabile
idhaben. - Eigenschaften (
properties) als einfache Werte – Zeichenketten, Zahlen, Booleans, Datumsangaben in ISO 8601. Kein HTML. - Zahlen als Zahlen (
1100), nicht als Zeichenketten ("1100 l"); die Einheit gehört infields[].unit.
Umwandlung einer GIS-Datei in GeoJSON:
ogr2ogr -f GeoJSON -t_srs EPSG:4326 -lco RFC7946=YES -lco COORDINATE_PRECISION=6 lavicky.geojson vstup.shp
Umrechnung von Koordinaten in Python:
from pyproj import Transformer
to_wgs84 = Transformer.from_crs("EPSG:31287", "EPSG:4326", always_xy=True)
lon, lat = to_wgs84.transform(x_lambert, y_lambert) # always_xy=True liefert [lon, lat]
4. Manifest
Füllen Sie nicht nur die Pflichtfelder aus, sondern auch jene, die darüber entscheiden, ob die Daten gefunden werden:
description– 1–2 Sätze: was die Daten enthalten, woher sie stammen, wie aktuell sie sind.keywords– 5–15 Wörter und Synonyme auf Deutsch, einschließlich umgangssprachlicher und regionaler Formen (MistkübelnebenMüllcontainer).useCases– wobei die Daten helfen:construction,housing,land-purchase,agriculture,investment,transport,tourism,business,public-administration. Danach wählt der Assistent des Portals die Daten zu Anfragen wie „Ich möchte in der Gemeinde X bauen“ aus.category– eine von:territory,nature,water,risks,energy,transport,society,economy,infrastructure.fields– jede Eigenschaft, die Nutzerinnen und Nutzer sehen sollen:label,type,unit. Markieren Sie eine alsprimary(Überschrift im Detail).license– Name und Attributionstext. Erfinden Sie keine Lizenz; wenn Sie sie nicht kennen, fragen Sie den Menschen.temporal.updated,temporal.frequency– Aktualität der Daten.temporal.param– bei zeitbezogenen Daten (Unfälle, Messungen, Ereignisse): der Schlüssel eines Parameters vom Typrangemit Jahren (min/max= erstes und letztes Jahr), mit dem der Zeitraum gewählt wird. Die Antwort sollte dazudm.timelinemit Anzahlen pro Jahr senden (bei Werten im Zeitverlauf wie der Einwohnerzahl stattdessen ein zusammenfassendesdm.chart), siehe Zeitfilter.- Entwicklung im Zeitverlauf – prüfen Sie, ob Objekte Werte für mehrere Zeiträume haben (Stände nach Jahren, Messungen, Preise). Wenn ja, senden Sie diese bei jedem Objekt immer in
dm.chart; geht es im Datensatz um die Veränderung, färben Sie nach Trend. Siehe Entwicklung im Zeitverlauf und Trend.
5. Visualisierung
| Geometrie | Geeigneter Typ | Hinweis |
|---|---|---|
| Punkte, Dutzende bis Hunderte | circle |
Farbe nach Kategorie (categorical), Beschriftung über label |
| Punkte, Tausende | circle mit cluster oder heatmap |
Heatmap nur für die Dichte, sie zeigt keine einzelnen Objekte |
| Linien | line |
die Breite kann von einem Wert abhängen |
| Flächen mit Wert | fill + interpolate oder step |
Choroplethenkarte; opacity 0.4–0.6, damit die Grundkarte sichtbar bleibt |
| Punkte oder Flächen, deren Wert sich im Zeitverlauf ändert | circle / fill + color: { "type": "trend" } |
wenn es um die Veränderung geht (steigt / sinkt); die Wertereihe in dm.chart bei jedem Objekt, den Trend berechnet das Portal |
| Flächen ohne Wert | fill mit einer Farbe |
Regeln: Farben als Hex; Kategorien max. ~8 Farben; für Werte eine sequenzielle Skala von hell nach dunkel; field in der Darstellung muss einer Eigenschaft in den Daten entsprechen (und in fields, mit numerischem Typ bei interpolate/step). Hat der Wert eines Objekts eine Historie, senden Sie diese bei jeder Darstellung in dm.chart – auch bei einer Choroplethenkarte mit Jahresauswahl (das gewählte Jahr in highlight).
6. Endpunkt (nur bei mode: "bbox" oder Live-Daten)
Implementieren Sie GET gemäß OpenAPI-Vertrag:
- Query
bbox=minLon,minLat,maxLon,maxLat– geben Sie die Objekte zurück, die den Ausschnitt schneiden, zoom– ganze Zahl; bei geringer Zoomstufe dürfen Sie Aggregationen zurückgeben,limit– geben Sie nicht mehr Objekte zurück; wenn Sie kürzen, setzen Siedm.truncated: trueunddm.total,- eigene Parameter aus
params(Bereichvon,bis, mehrere Wertea,b,c), Content-Type: application/geo+jsonoderapplication/json, HTTPS, Antwort innerhalb von 1 s,- unbekannte Query-Parameter ignorieren (
lang,protocol,cursorkönnen Sie übergehen).
CORS ist nicht nötig – das Portal fragt vom Server aus ab.
Python (FastAPI + PostGIS):
from fastapi import FastAPI
app = FastAPI()
@app.get("/datovamapa/kontejnery")
def kontejnery(bbox: str | None = None, zoom: int | None = None, limit: int = 5000, druh: str | None = None):
west, south, east, north = map(float, bbox.split(",")) if bbox else (-180, -90, 180, 90)
druhy = druh.split(",") if druh else None
rows = db.fetch_all(
"""
SELECT id, druh, objem, vyvoz::text, ST_AsGeoJSON(geom, 6)::json AS geometry
FROM kontejnery
WHERE geom && ST_MakeEnvelope(%s, %s, %s, %s, 4326)
AND (%s::text[] IS NULL OR druh = ANY(%s))
LIMIT %s
""",
(west, south, east, north, druhy, druhy, limit + 1),
)
features = [
{"type": "Feature", "id": r["id"], "geometry": r["geometry"],
"properties": {"druh": r["druh"], "objem": r["objem"], "vyvoz": r["vyvoz"]}}
for r in rows[:limit]
]
return {"type": "FeatureCollection", "features": features, "dm": {"truncated": len(rows) > limit, "ttl": 3600}}
TypeScript (Cloudflare Worker / Node, Daten im Speicher):
// KONTEJNERY: Array von GeoJSON-Features mit Punkten, beim Start geladen (aus der Datenbank oder einer Datei).
declare const KONTEJNERY: { type: "Feature"; id: number; geometry: { type: "Point"; coordinates: [number, number] }; properties: Record<string, unknown> }[];
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
const [west, south, east, north] = (url.searchParams.get("bbox") ?? "-180,-90,180,90").split(",").map(Number);
const limit = Math.min(Number(url.searchParams.get("limit")) || 5000, 5000);
const inside = KONTEJNERY.filter(({ geometry: { coordinates: [lon, lat] } }) =>
lon >= west && lon <= east && lat >= south && lat <= north);
return Response.json(
{ type: "FeatureCollection", features: inside.slice(0, limit), dm: { total: inside.length, truncated: inside.length > limit } },
{ headers: { "Content-Type": "application/geo+json", "Cache-Control": "public, max-age=300" } },
);
},
};
7. Validierung
Wiederholen Sie, bis keine Fehler mehr auftreten, und gehen Sie jede Warnung durch (beheben oder bewusst belassen und begründen):
# Manifest (ohne Veröffentlichung)
curl -sX POST https://datenkarte.at/api/validate -H 'Content-Type: application/json' --data @manifest.json
# Daten gegen einen bestimmten Datensatz
curl -sX POST https://datenkarte.at/api/validate -H 'Content-Type: application/json' \
--data "{\"data\": $(cat lavicky.geojson), \"dataset\": $(jq '.datasets[0]' manifest.json)}"
# Live-Test des veröffentlichten Manifests und aller Quellen
curl -s 'https://datenkarte.at/api/validate?manifestUrl=https://www.gemeinde-beispiel.at/datenkarte/manifest.json'
Antwort: { valid, summary, errors: [{ path, code, message, hint? }], warnings: [...] }, beim Live-Test zusätzlich datasets[] mit Abfrage, Antwortzeit und Anzahl der Objekte. Die Bedeutung der Codes steht in der API des Portals.
8. Übergabe an den Menschen
Fassen Sie zum Schluss zusammen:
- URL des Manifests und Liste der Datensätze (id, Titel, Quelltyp, Anzahl der Objekte),
- Ergebnis des Live-Tests (
summary, verbleibende Warnungen und warum sie bleiben), - was der Mensch bestätigen muss: Lizenz und Attribution, dass die Daten keine personenbezogenen Daten enthalten, Kontakt für das Portal,
- wie die Daten aktualisiert werden (Skript, Cron, maßgebliche Quelle).
Sobald der Mensch Lizenz und Inhalt bestätigt hat, registrieren Sie das Manifest: POST https://datenkarte.at/api/registration mit { "manifestUrl": "…" } oder über das MCP-Werkzeug register_provider. Die Daten sind dann als ungeprüft verfügbar (mit Warnung, außerhalb des Katalogs), bis der Betreiber sie freigibt. Nach jeder Änderung des Manifests warten sie auf eine neue Freigabe. Einzelheiten: Registrierung.
Regeln
- Veröffentlichen Sie keine personenbezogenen Daten (DSGVO). Im Zweifel aggregieren oder weglassen und nachfragen.
- Erfinden Sie weder Lizenz noch Quelle noch Aktualität der Daten.
- Fügen Sie in
propertiesunddmkein HTML ein; das Portal zeigt alles als Text an. - Keine geheimen Schlüssel in URLs – das Manifest ist öffentlich.
- Ändern Sie
provider.idunddataset.idnach der Veröffentlichung nicht. - Verwenden Sie die Schlüssel
bbox,zoom,limit,cursor,lang,protocolnicht für eigene Parameter. - Max. 5 000 Objekte und 15 MB pro Antwort, Antwort innerhalb von 10 s (Ziel ist unter 1 s).
Abnahmekriterien
- Das Manifest ist über HTTPS erreichbar und der Live-Test des Validators liefert
"valid": true. - Jeder Datensatz hat
description,keywords,useCases,categoryund (bei GeoJSON)fields. - Lizenz und Attribution sind vom Menschen bestätigt.
- Die Koordinaten sind WGS84
[lon, lat], keine Warnungswapped_coordinatesoderoutside_coverage. - Kein Datensatz liefert im Test 0 Objekte (
empty_response), sofern das nicht beabsichtigt ist. - Objekte mit Werten für mehrere Zeiträume senden
dm.chart(keinmissing_trend_chartund keintrend_without_chart). - Die Antwortzeit der Endpunkte liegt unter 3 s (
slow_response). - Die Daten enthalten keine personenbezogenen Daten.
- Es ist beschrieben, wie die Daten aktualisiert werden.
Auftrag für den Agenten (zum Kopieren)
Ein Mensch kann seinem Agenten diesen Text geben:
Bereite unsere räumlichen Daten für das Portal Datenkarte (https://datenkarte.at) auf.
Gehe nach der Anleitung https://datenkarte.at/dokumentation/fuer-ki-agenten.md vor
(Übersicht der Dokumentation: https://datenkarte.at/llms.txt, MCP-Server: https://datenkarte.at/mcp).
Datenquellen: <Datenbanken, Dateien, Dienste beschreiben>.
Wo veröffentlichen: <Website / Server / Repository>.
Lizenz: <z. B. CC BY 4.0, Attribution „© Gemeinde Beispiel“> – erfinde keine andere.
Fertig ist es, wenn der Live-Test des Validators valid: true liefert und du die Abnahmekriterien erfüllt hast.
Übergib mir am Ende eine Zusammenfassung gemäß Schritt 8.