{
  "components": {
    "parameters": {
      "LatParam": {
        "description": "Latitude (Breitengrad) für WGS84-Koordinaten",
        "example": 52.52,
        "in": "query",
        "name": "lat",
        "schema": {
          "format": "double",
          "maximum": 90,
          "minimum": -90,
          "type": "number"
        }
      },
      "LonParam": {
        "description": "Longitude (Längengrad) für WGS84-Koordinaten",
        "example": 13.405,
        "in": "query",
        "name": "lon",
        "schema": {
          "format": "double",
          "maximum": 180,
          "minimum": -180,
          "type": "number"
        }
      },
      "MgrsParam": {
        "description": "Koordinate als MGRS-Referenz (Military Grid Reference System), variabler\nGenauigkeit von 1 bis 5 Ziffern je Achse (10 km bis 1 m). Leerzeichen\nzwischen Zone/Band, 100-km-Quadrat und den Zifferngruppen sind optional\n(\"32UNA0123456789\" und \"32U NA 01234 56789\" sind gleichwertig). Bei\ngeringerer Genauigkeit wird der Südwest-Ursprung der jeweiligen Zelle\ngeliefert, nicht deren Mittelpunkt.\n\nIst `mgrs` gesetzt, hat es Vorrang vor `lon`/`lat`/`x`/`y`/`srid` im\nselben Request — die Zielkoordinate erhält die zur MGRS-Zone passende\nUTM-SRID (EPSG:32601-32660 nördliche, 32701-32760 südliche Hemisphäre),\n`srid` wird für diesen Fall ignoriert.\n\nNur die UTM-Breitenbänder C-X (±80° Breite) werden unterstützt; die\npolaren Bänder A, B, Y, Z (UPS-Projektion) sind nicht im Funktionsumfang.\n",
        "example": "32U NA 01234 56789",
        "in": "query",
        "name": "mgrs",
        "schema": {
          "type": "string"
        }
      },
      "PropertiesParam": {
        "description": "Komma-separierte Liste von Eigenschaften, die zurückgegeben werden sollen.\nWenn nicht angegeben, werden alle Eigenschaften zurückgegeben.\n",
        "example": "name,population",
        "in": "query",
        "name": "properties",
        "schema": {
          "type": "string"
        }
      },
      "SourceIdParam": {
        "description": "Die eindeutige ID der Datenquelle",
        "example": "districts",
        "in": "path",
        "name": "sourceId",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "SridParam": {
        "description": "Spatial Reference ID der Eingabekoordinaten.\nStandard ist 4326 (WGS84).\n",
        "example": 4326,
        "in": "query",
        "name": "srid",
        "schema": {
          "default": 4326,
          "type": "integer"
        }
      },
      "WithGazetteerParam": {
        "description": "Steuert die Gazetteer-Anreicherung der Antwort um einen `gazetteer`-Block\n(Verwaltungshierarchie, Peilung, name_source-Erläuterungen und Datensatz-\nAttribution). **Standard: an**, sofern das Gazetteer-Feature aktiv ist — so\nerhält ein Client alle Infos in einem Aufruf. Reines Opt-out: die falsy\nWerte (`0`/`false`/`no`/`off`) schalten die Anreicherung ab, die übrigen\nWerte lassen sie an. (Der Server ist tolerant und behandelt jeden nicht-\nfalsy Wert als „an\"; die aufgezählten Werte sind die unterstützten.)\nBest-effort: ein Gazetteer-Fehler bricht die eigentliche Abfrage nicht ab\n(der Block entfällt dann).\n",
        "example": "0",
        "in": "query",
        "name": "with-gazetteer",
        "schema": {
          "default": "1",
          "enum": [
            "0",
            "1",
            "true",
            "false",
            "yes",
            "no",
            "on",
            "off"
          ],
          "type": "string"
        }
      },
      "XParam": {
        "description": "X-Koordinate für andere Koordinatensysteme",
        "example": 389283,
        "in": "query",
        "name": "x",
        "schema": {
          "format": "double",
          "type": "number"
        }
      },
      "YParam": {
        "description": "Y-Koordinate für andere Koordinatensysteme",
        "example": 5819450,
        "in": "query",
        "name": "y",
        "schema": {
          "format": "double",
          "type": "number"
        }
      }
    },
    "schemas": {
      "BatchQueryPoint": {
        "description": "Ein Abfragepunkt mit optionaler, vom Client gewählter Echo-id. Koordinaten als mgrs ODER lon/lat ODER x/y; ein optionaler srid überschreibt den Batch-srid (wirkt nicht auf mgrs — siehe dessen Beschreibung).",
        "properties": {
          "id": {
            "description": "Opaque Echo-id (Eindeutigkeit ist Sache des Callers; fehlt → 0-basierter Index)",
            "type": "string"
          },
          "lat": {
            "format": "double",
            "type": "number"
          },
          "lon": {
            "format": "double",
            "type": "number"
          },
          "mgrs": {
            "description": "MGRS-Referenz, siehe die Beschreibung des mgrs-Query-Parameters bei GET /api/v1/query. Hat Vorrang vor lon/lat und x/y desselben Punkts; der Batch-srid (und ein per-Punkt srid) wird für diesen Punkt ignoriert, da mgrs seine eigene UTM-SRID mitbringt.",
            "example": "32U NA 01234 56789",
            "type": "string"
          },
          "srid": {
            "type": "integer"
          },
          "x": {
            "format": "double",
            "type": "number"
          },
          "y": {
            "format": "double",
            "type": "number"
          }
        },
        "type": "object"
      },
      "BatchQueryRequest": {
        "properties": {
          "points": {
            "description": "Die abzufragenden Punkte (\u003e= 1)",
            "items": {
              "$ref": "#/components/schemas/BatchQueryPoint"
            },
            "type": "array"
          },
          "properties": {
            "description": "Optional — nur diese Feature-Properties zurückgeben",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "sources": {
            "description": "Optional — nur diese Datenquellen abfragen (leer = alle)",
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "srid": {
            "description": "Standard-SRID für Punkte ohne eigenen srid (Default 4326)",
            "type": "integer"
          },
          "with-gazetteer": {
            "default": true,
            "description": "Gazetteer-Anreicherung pro Punkt. Default true (konsistent mit /query, das opt-out ist) — der teure Pfad; mit `false` explizit abschalten.",
            "type": "boolean"
          }
        },
        "required": [
          "points"
        ],
        "type": "object"
      },
      "BatchQueryResponse": {
        "description": "Sync-Antwort der Stapelabfrage (ein Item pro Eingabepunkt, in Reihenfolge)",
        "properties": {
          "processing_time_ms": {
            "format": "int64",
            "type": "integer"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/BatchQueryResultItem"
            },
            "type": "array"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "results",
          "total"
        ],
        "type": "object"
      },
      "BatchQueryResultItem": {
        "description": "Ein Batch-Ergebnis: wie eine Einzelpunkt-Antwort (coordinate, wgs84, results, optional gazetteer) plus die Echo-id — ODER ein error-Objekt, wenn der Punkt nicht auflösbar war. Im NDJSON-Stream ist jede Zeile genau ein solches Objekt.",
        "properties": {
          "coordinate": {
            "$ref": "#/components/schemas/Coordinate"
          },
          "error": {
            "description": "Nur gesetzt, wenn dieser Punkt nicht aufgelöst werden konnte",
            "nullable": true,
            "properties": {
              "message": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "gazetteer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GazetteerData"
              }
            ],
            "nullable": true
          },
          "id": {
            "type": "string"
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/QueryResult"
            },
            "type": "array"
          },
          "total_features": {
            "type": "integer"
          },
          "wgs84": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Wgs84Coordinate"
              }
            ],
            "nullable": true
          }
        },
        "type": "object"
      },
      "Coordinate": {
        "description": "Geografische Koordinate",
        "properties": {
          "srid": {
            "description": "Spatial Reference ID",
            "type": "integer"
          },
          "x": {
            "description": "X-Koordinate (Longitude bei WGS84)",
            "format": "double",
            "type": "number"
          },
          "y": {
            "description": "Y-Koordinate (Latitude bei WGS84)",
            "format": "double",
            "type": "number"
          }
        },
        "required": [
          "x",
          "y",
          "srid"
        ],
        "type": "object"
      },
      "Error": {
        "description": "Fehlermeldung",
        "properties": {
          "error": {
            "description": "HTTP-Statustext",
            "type": "string"
          },
          "message": {
            "description": "Detaillierte Fehlermeldung",
            "type": "string"
          }
        },
        "required": [
          "error",
          "message"
        ],
        "type": "object"
      },
      "Extent": {
        "description": "Räumliche Ausdehnung (Bounding Box)",
        "properties": {
          "max_x": {
            "format": "double",
            "type": "number"
          },
          "max_y": {
            "format": "double",
            "type": "number"
          },
          "min_x": {
            "format": "double",
            "type": "number"
          },
          "min_y": {
            "format": "double",
            "type": "number"
          }
        },
        "required": [
          "min_x",
          "min_y",
          "max_x",
          "max_y"
        ],
        "type": "object"
      },
      "Feature": {
        "description": "Geografisches Feature mit Eigenschaften",
        "properties": {
          "geometry": {
            "$ref": "#/components/schemas/Geometry"
          },
          "id": {
            "description": "Feature-ID",
            "format": "int64",
            "type": "integer"
          },
          "layer": {
            "description": "Name des Layers",
            "type": "string"
          },
          "properties": {
            "additionalProperties": true,
            "description": "Schlüssel-Wert-Paare der Feature-Eigenschaften",
            "type": "object"
          }
        },
        "required": [
          "id",
          "layer",
          "properties"
        ],
        "type": "object"
      },
      "GazetteerData": {
        "description": "Gazetteer-Anreicherung für eine Koordinate: Verwaltungshierarchie (admin), Insel(n) (islands), Peilung (bearing), Geländeexposition (exposure), Höhe über NN (elevation), name_source-Erläuterungen (sources) und Datensatz-Lizenz/Attribution (license). Geliefert von GET /api/v1/gazetteer und — standardmäßig — im `gazetteer`-Block von GET /api/v1/query.",
        "properties": {
          "admin": {
            "description": "Verwaltungshierarchie, die die Koordinate enthält (null ohne Abdeckung).",
            "nullable": true,
            "properties": {
              "country_iso": {
                "description": "ISO 3166-1 alpha-2 Ländercode.",
                "type": "string"
              },
              "hierarchy": {
                "description": "Ebenen, lokalste zuerst.",
                "items": {
                  "properties": {
                    "equivalent": {
                      "description": "Semantische Bedeutung (country | state | … | municipality).",
                      "type": "string"
                    },
                    "equivalent_description": {
                      "description": "Generische Beschreibung des equivalent-Levels.",
                      "type": "string"
                    },
                    "level": {
                      "description": "OSM admin_level.",
                      "type": "integer"
                    },
                    "local_term": {
                      "description": "Landesspezifischer Begriff für diese Ebene (z. B. „Landkreis\").",
                      "type": "string"
                    },
                    "name": {
                      "description": "Name in lateinischer Schrift (immer gesetzt).",
                      "type": "string"
                    },
                    "name_native": {
                      "description": "Name in Originalschrift (leer, wenn identisch mit name).",
                      "type": "string"
                    },
                    "name_source": {
                      "description": "Codierung der Namensherkunft/Romanisierung; im response-weiten sources-Block beschrieben. Leer, wenn keine.",
                      "type": "string"
                    }
                  },
                  "type": "object"
                },
                "type": "array"
              }
            },
            "type": "object"
          },
          "available": {
            "description": "Welche der optionalen Blöcke dieses Deployment überhaupt beantworten kann. Jeder optionale Block ist in zwei unabhängigen Fällen null: das Feature gehört nicht zu diesem Datensatz (Layer fehlt im Paket, kein DEM verdrahtet), oder es gehört dazu und der Punkt hat einfach kein Ergebnis. Ohne diese Angabe sind beide Fälle nicht unterscheidbar — und der zweite ist normal, denn ein Punkt in der Ebene liegt berechtigt auf keinem Berg. true heißt \"dieses Deployment kann den Block beantworten\" und sagt nichts darüber, ob der angefragte Punkt ein Ergebnis hat.",
            "properties": {
              "elevation": {
                "description": "DEM verdrahtet, Höhe kann gesampelt werden.",
                "type": "boolean"
              },
              "exposure": {
                "description": "Slope/Aspect ableitbar (setzt dasselbe DEM voraus wie elevation).",
                "type": "boolean"
              },
              "islands": {
                "description": "Islands-Layer ist deklariert und im GeoPackage vorhanden.",
                "type": "boolean"
              },
              "mountains": {
                "description": "Mountains-Layer ist deklariert und im GeoPackage vorhanden.",
                "type": "boolean"
              }
            },
            "required": [
              "islands",
              "mountains",
              "exposure",
              "elevation"
            ],
            "type": "object"
          },
          "bearing": {
            "description": "Peilung zum salientesten nahen Ort (null ohne Anker in Reichweite).",
            "nullable": true,
            "properties": {
              "azimuth": {
                "description": "Azimut Referenz→Punkt in Grad (0=N, 90=O).",
                "format": "double",
                "type": "number"
              },
              "class": {
                "description": "OSM place-Klasse (village | town | city).",
                "type": "string"
              },
              "compass": {
                "description": "Quantisierte Himmelsrichtung (z. B. E, NW). Leer, wenn `inside`.",
                "type": "string"
              },
              "distance_km": {
                "format": "double",
                "type": "number"
              },
              "inside": {
                "description": "Der Abfragepunkt liegt IN einem Ort („in X\"), nicht nur in der Nähe („prope X\"). Bestimmt über die Distanz zu einem Ort, dessen klassenskalierter Radius (Stadt 3 km, Kleinstadt 1,5 km, Dorf 0,8 km — Proxy für die Siedlungsausdehnung) den Punkt abdeckt; der nächste solche Ort gewinnt (nicht zwingend der überhaupt nächste Ort). NICHT per Verwaltungs-Containment (ein Gemeindegebiet ist groß und ländlich, meldete also Feld/Wald kilometerweit vom Ort fälschlich als „in X\"). Ein Client kann bei `true` die Peilung weglassen (der Fund liegt direkt im Ort).",
                "type": "boolean"
              },
              "label": {
                "description": "Fertiges Label: „4 km E Würzburg\", „prope Würzburg\" (nah, außerhalb) oder „in Würzburg\" (`inside`). Präfixe folgen der Etiketten-Konvention: lateinisch „in\" bzw. „prope\" (= „bei/nahe\", etablierter Fundort-Terminus).",
                "type": "string"
              },
              "name_native": {
                "description": "Name des Anker-Orts in Originalschrift (leer, wenn identisch).",
                "type": "string"
              },
              "name_source": {
                "description": "Codierung der Namensherkunft/Romanisierung des Ankers; im response-weiten sources-Block beschrieben. Leer, wenn keine.",
                "type": "string"
              },
              "reference": {
                "description": "Name des Anker-Orts (lateinische Schrift).",
                "type": "string"
              }
            },
            "type": "object"
          },
          "elevation": {
            "description": "Höhe über NN am Abfragepunkt, aus einem kontinuierlichen Höhen-Raster (DEM).\nDer Schlüssel ist immer vorhanden; `null` bedeutet **keine Aussage**: entweder ist kein DEM konfiguriert — dann meldet `available.elevation` false — oder der Punkt liegt außerhalb der DEM-Abdeckung. Letzteres galt früher als `sea_level: true`; das war eine Konvention, die nicht trägt, denn der Rand eines DEM verläuft ebenso oft durch Land wie durch Wasser, und 0 m wurde damit genau dort behauptet, wo die Daten enden.\n`sea_level: true` heißt jetzt: das DEM **deckt** den Punkt ab und hat dort keinen Wert — vermessenes Wasser. `meters` ist dann 0 und `accuracy_basis` weist die Konvention aus.\nFür abgeleitete Größen (Hangneigung/Exposition) ist der *relative* Fehler maßgeblich — siehe accuracy_basis.",
            "nullable": true,
            "properties": {
              "accuracy_basis": {
                "description": "Grundlage von accuracy_m, z. B. \"GLO-30 LE90 (absolute)\" oder \"Copernicus HEM (per-pixel 1σ)\".",
                "type": "string"
              },
              "accuracy_m": {
                "description": "Vertikale Genauigkeit in Metern. Aktuell eine datensatzweite Konstante (LE90); künftig per-Punkt aus der Height Error Mask (HEM).",
                "format": "double",
                "type": "number"
              },
              "horizontal_accuracy_m": {
                "description": "Horizontale Genauigkeit in Metern (LE90).",
                "format": "double",
                "type": "number"
              },
              "meters": {
                "description": "Orthometrische Höhe über NN in Metern.",
                "format": "double",
                "type": "number"
              },
              "sea_level": {
                "description": "true, wenn das DEM den Punkt **abdeckt** und dort keinen Wert hat — vermessenes Wasser; meters ist dann 0 (Meeresspiegel-Konvention). Für einen Punkt AUSSERHALB der DEM-Abdeckung ist stattdessen der gesamte elevation-Block null, denn dort ist die Höhe unbekannt.",
                "type": "boolean"
              },
              "source": {
                "description": "Lizenz/Attribution der DEM-Quelle (z. B. Copernicus GLO-30), getrennt von der Gazetteer-license. Muss ein Client anzeigen.",
                "nullable": true,
                "properties": {
                  "attribution": {
                    "description": "Anzuzeigender Attributionstext.",
                    "type": "string"
                  },
                  "name": {
                    "description": "Lizenzname (SPDX-Id oder Bezeichnung).",
                    "type": "string"
                  },
                  "url": {
                    "description": "Link zum Lizenztext bzw. zur Quelle.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "surface_model": {
                "description": "Oberflächenmodell-Hinweis, z. B. \"DSM\" (Oberfläche inkl. Vegetation/Bebauung, nicht bare-earth).",
                "type": "string"
              },
              "vertical_datum": {
                "description": "Vertikaler Bezug, z. B. \"EGM2008\".",
                "type": "string"
              }
            },
            "type": "object"
          },
          "exposure": {
            "description": "Geländeexposition am Abfragepunkt: Hangneigung (slope) und Expositionsrichtung (aspect), aus dem DEM per Horn-Verfahren über eine 3×3-Nachbarschaft. null, wenn kein DEM konfiguriert ist oder der Punkt (bzw. ein Nachbar) keine DEM-Abdeckung hat. Da die Quelle ein DSM ist (Oberfläche inkl. Vegetation/Bebauung), spiegeln Neigung/Richtung über Wald/Bebauung die Kronen/Dächer, nicht das Gelände; auf flachem Grund ist die Richtung rauschbestimmt (siehe `flat`).",
            "nullable": true,
            "properties": {
              "aspect_compass": {
                "description": "Quantisierte Exposition (N, NE, …, 8-Punkt-Rose). Leer, wenn `flat`.",
                "type": "string"
              },
              "aspect_deg": {
                "description": "Expositions-Azimut in Grad (0=N, 90=O, im Uhrzeigersinn), also die Richtung, in die der Hang abfällt. null, wenn `flat` (Richtung unbestimmt).",
                "format": "double",
                "nullable": true,
                "type": "number"
              },
              "flat": {
                "description": "true, wenn die Neigung unter der Flach-Schwelle (~2°) liegt; die Expositionsrichtung ist dann rauschbestimmt und wird weggelassen.",
                "type": "boolean"
              },
              "sample_spacing_m": {
                "description": "Horizontaler Abstand der Gradient-Stützpunkte in Metern (die Horn-Basislänge ist das Doppelte); richtet sich nach der DEM-Auflösung (GLO-30 ≈ 30 m).",
                "format": "double",
                "type": "number"
              },
              "slope_deg": {
                "description": "Hangneigung in Grad (0 = eben, 90 = senkrecht).",
                "format": "double",
                "type": "number"
              },
              "slope_percent": {
                "description": "Hangneigung als Prozent-Gefälle (100·tan(slope)).",
                "format": "double",
                "type": "number"
              },
              "source": {
                "description": "DEM-Quelle (Lizenz/Attribution), wie bei elevation.",
                "nullable": true,
                "properties": {
                  "attribution": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                },
                "type": "object"
              }
            },
            "type": "object"
          },
          "islands": {
            "description": "Insel(n), deren Polygon die Koordinate enthält (eigener Layer, unabhängig von der Verwaltungsabdeckung aufgelöst). null, wenn der Punkt auf keiner erfassten Insel liegt oder kein Insel-Layer konfiguriert ist. Mehrere Einträge bei verschachtelten Inseln.",
            "items": {
              "properties": {
                "name": {
                  "description": "Inselname in lateinischer Schrift (immer gesetzt).",
                  "type": "string"
                },
                "name_native": {
                  "description": "Inselname in Originalschrift (leer, wenn identisch mit name).",
                  "type": "string"
                },
                "name_source": {
                  "description": "Codierung der Namensherkunft/Romanisierung; im response-weiten sources-Block beschrieben. Leer, wenn keine.",
                  "type": "string"
                }
              },
              "type": "object"
            },
            "nullable": true,
            "type": "array"
          },
          "license": {
            "description": "Datensatzweite Lizenz/Attribution der Gazetteer-Daten (z. B. OSM/ODbL, GeoNames, Natural Earth), aus dem Manifest (ortus-gazetteer.yaml). Fehlt, wenn im Manifest keine Lizenz gesetzt ist. Diese Attribution muss ein Client anzeigen.",
            "nullable": true,
            "properties": {
              "attribution": {
                "description": "Anzuzeigender Attributionstext.",
                "type": "string"
              },
              "name": {
                "description": "Lizenzname (SPDX-Id oder Bezeichnung), z. B. \"ODbL-1.0\".",
                "type": "string"
              },
              "url": {
                "description": "Link zum Lizenztext.",
                "type": "string"
              }
            },
            "type": "object"
          },
          "mountains": {
            "description": "Berg/Gebirge, in dem die Koordinate liegt (eigener Layer, unabhängig von der Verwaltungsabdeckung). null, wenn der Punkt auf keinem erfassten Berg/Gebirge liegt oder kein Mountains-Layer konfiguriert ist. Das jeweils kleinste (spezifischste) enthaltende Feature PRO landform: der Einzelberg (mountain) und der Gebirgszug (range) getrennt (z. B. mountain: Schwanberg, range: Steigerwald).",
            "nullable": true,
            "properties": {
              "mountain": {
                "description": "Kleinster enthaltender Einzelberg (DEM-Territorium). null, wenn keiner enthält oder die Einzelberg-Zeilen fehlen (optionaler Aufbauschritt im Datensatz).",
                "nullable": true,
                "properties": {
                  "elevation": {
                    "description": "Gipfelhöhe in Metern (nur beim Einzelberg gesetzt).",
                    "format": "double",
                    "type": "number"
                  },
                  "name": {
                    "description": "Bergname in lateinischer Schrift (immer gesetzt).",
                    "type": "string"
                  },
                  "name_native": {
                    "description": "Bergname in Originalschrift (leer, wenn identisch mit name).",
                    "type": "string"
                  },
                  "name_source": {
                    "description": "Codierung der Namensherkunft; im sources-Block beschrieben.",
                    "type": "string"
                  }
                },
                "type": "object"
              },
              "range": {
                "description": "Kleinster enthaltender Gebirgszug. null, wenn keiner enthält.",
                "nullable": true,
                "properties": {
                  "name": {
                    "description": "Gebirgsname in lateinischer Schrift (immer gesetzt).",
                    "type": "string"
                  },
                  "name_native": {
                    "description": "Gebirgsname in Originalschrift (leer, wenn identisch mit name).",
                    "type": "string"
                  },
                  "name_source": {
                    "description": "Codierung der Namensherkunft; im sources-Block beschrieben.",
                    "type": "string"
                  }
                },
                "type": "object"
              }
            },
            "type": "object"
          },
          "sources": {
            "description": "Response-weiter Herkunfts-Auszug: jede in admin/bearing vorkommende name_source-Codierung wird hier genau einmal beschrieben (nicht pro Datensatz wiederholt). Leeres Array, wenn keine Codierungen gesetzt sind.",
            "items": {
              "properties": {
                "code": {
                  "description": "Codierung, auf die name_source verweist.",
                  "type": "string"
                },
                "long": {
                  "description": "Ausführliche Beschreibung der Quelle/Codierung.",
                  "type": "string"
                },
                "short": {
                  "description": "Kurzbezeichnung der Quelle/Codierung.",
                  "type": "string"
                },
                "standard": {
                  "description": "Zugrundeliegender Standard bzw. Zitierhinweis.",
                  "type": "string"
                }
              },
              "type": "object"
            },
            "type": "array"
          },
          "timings_ms": {
            "description": "Wall-clock-Zeit der einzelnen Enrichment-Sektionen in Millisekunden. Ergänzt processing_time_ms des /query-Endpoints (das nur die generischen Quell-Abfragen misst und dieses Enrichment ausschließt), damit die oft dominante DEM-abgeleitete exposure/elevation-Zeit sichtbar ist.",
            "properties": {
              "bearing": {
                "type": "integer"
              },
              "elevation": {
                "type": "integer"
              },
              "exposure": {
                "type": "integer"
              },
              "islands": {
                "type": "integer"
              },
              "locate": {
                "type": "integer"
              },
              "mountains": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              }
            },
            "type": "object"
          }
        },
        "required": [
          "admin",
          "islands",
          "mountains",
          "bearing",
          "exposure",
          "elevation",
          "available",
          "sources",
          "timings_ms"
        ],
        "type": "object"
      },
      "GazetteerResponse": {
        "allOf": [
          {
            "properties": {
              "coordinate": {
                "$ref": "#/components/schemas/Coordinate"
              },
              "wgs84": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/Wgs84Coordinate"
                  }
                ],
                "description": "Der Abfragepunkt in WGS84 (lon/lat); bei projiziertem `srid` reprojiziert (siehe with-gazetteer / Endpoint-Beschreibung).",
                "nullable": true
              }
            },
            "required": [
              "coordinate"
            ],
            "type": "object"
          },
          {
            "$ref": "#/components/schemas/GazetteerData"
          }
        ],
        "description": "Reverse-Geocoding- und Peilungs-Ergebnis für eine Koordinate.",
        "type": "object"
      },
      "Geometry": {
        "description": "Geometrie im WKT-Format",
        "properties": {
          "type": {
            "description": "Geometrietyp",
            "enum": [
              "POINT",
              "LINESTRING",
              "POLYGON",
              "MULTIPOINT",
              "MULTILINESTRING",
              "MULTIPOLYGON",
              "GEOMETRYCOLLECTION"
            ],
            "type": "string"
          },
          "wkt": {
            "description": "Well-Known Text Repräsentation",
            "type": "string"
          }
        },
        "required": [
          "type",
          "wkt"
        ],
        "type": "object"
      },
      "HealthStatus": {
        "description": "Detaillierter Gesundheitsstatus",
        "properties": {
          "components": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Status einzelner Komponenten",
            "type": "object"
          },
          "ready": {
            "description": "Service ist bereit für Anfragen",
            "type": "boolean"
          },
          "sources": {
            "description": "Status je Datenquelle.",
            "items": {
              "properties": {
                "id": {
                  "description": "Datenquellen-ID.",
                  "type": "string"
                },
                "ready": {
                  "description": "Datenquelle ist abfragebereit.",
                  "type": "boolean"
                },
                "status": {
                  "description": "Lebenszyklus-Status der Datenquelle.",
                  "enum": [
                    "loading",
                    "indexing",
                    "ready",
                    "error",
                    "unloading"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "id",
                "status",
                "ready"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "sources_loaded": {
            "description": "Anzahl geladener Datenquellen",
            "type": "integer"
          },
          "sources_ready": {
            "description": "Anzahl abfragebereiter Datenquellen",
            "type": "integer"
          },
          "status": {
            "enum": [
              "ok",
              "unhealthy"
            ],
            "type": "string"
          }
        },
        "required": [
          "status",
          "ready",
          "sources_loaded",
          "sources_ready",
          "sources",
          "components"
        ],
        "type": "object"
      },
      "Layer": {
        "description": "Datenquellen-Layer",
        "properties": {
          "description": {
            "description": "Beschreibung des Layers",
            "type": "string"
          },
          "extent": {
            "$ref": "#/components/schemas/Extent"
          },
          "feature_count": {
            "description": "Anzahl der Features",
            "type": "integer"
          },
          "geometry_column": {
            "description": "Name der Geometriespalte",
            "type": "string"
          },
          "geometry_type": {
            "description": "Geometrietyp",
            "enum": [
              "POINT",
              "LINESTRING",
              "POLYGON",
              "MULTIPOINT",
              "MULTILINESTRING",
              "MULTIPOLYGON",
              "GEOMETRYCOLLECTION"
            ],
            "type": "string"
          },
          "has_index": {
            "description": "Hat einen räumlichen Index",
            "type": "boolean"
          },
          "name": {
            "description": "Name des Layers",
            "type": "string"
          },
          "srid": {
            "description": "Spatial Reference ID",
            "type": "integer"
          }
        },
        "required": [
          "name",
          "geometry_type",
          "geometry_column",
          "srid",
          "has_index",
          "feature_count"
        ],
        "type": "object"
      },
      "LayerList": {
        "description": "Liste von Layern",
        "properties": {
          "count": {
            "description": "Anzahl der Layer",
            "type": "integer"
          },
          "layers": {
            "items": {
              "$ref": "#/components/schemas/Layer"
            },
            "type": "array"
          },
          "source_id": {
            "description": "ID der Datenquelle",
            "type": "string"
          }
        },
        "required": [
          "source_id",
          "layers",
          "count"
        ],
        "type": "object"
      },
      "License": {
        "description": "Lizenzinformationen",
        "properties": {
          "attribution": {
            "description": "Attributionstext",
            "type": "string"
          },
          "name": {
            "description": "Name der Lizenz",
            "type": "string"
          },
          "url": {
            "description": "URL zur Lizenz",
            "format": "uri",
            "type": "string"
          }
        },
        "type": "object"
      },
      "LivenessStatus": {
        "description": "Liveness-Status (GET /health/live).",
        "properties": {
          "status": {
            "enum": [
              "ok",
              "unhealthy"
            ],
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "QueryResponse": {
        "description": "Antwort von GET /api/v1/query (Sammelabfrage über alle Quellen), inklusive dem optionalen gazetteer-Block. GET /api/v1/query/{sourceId} liefert diesen Block NICHT (siehe QueryResponsePerSource).",
        "properties": {
          "coordinate": {
            "$ref": "#/components/schemas/Coordinate"
          },
          "gazetteer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/GazetteerData"
              }
            ],
            "description": "Gazetteer-Anreicherung (admin, islands, bearing, exposure, elevation, sources, license). Standardmäßig enthalten, wenn das Gazetteer-Feature aktiv ist und der Abfragepunkt nach WGS84 auflösbar ist (ein projizierter `srid` wird dafür reprojiziert) — mit with-gazetteer=0 (false/no/off) abschaltbar. Fehlt, wenn das Feature aus ist, abgeschaltet wurde oder der `srid` sich nicht nach WGS84 transformieren lässt.",
            "nullable": true
          },
          "processing_time_ms": {
            "description": "Gesamte Verarbeitungszeit in Millisekunden",
            "format": "int64",
            "type": "integer"
          },
          "results": {
            "description": "Ergebnisse pro Datenquelle",
            "items": {
              "$ref": "#/components/schemas/QueryResult"
            },
            "type": "array"
          },
          "total_features": {
            "description": "Gesamtanzahl der gefundenen Features",
            "type": "integer"
          },
          "wgs84": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Wgs84Coordinate"
              }
            ],
            "description": "Der Abfragepunkt in WGS84 (lon/lat) — die Eingabekoordinate, bei einem projizierten `srid` nach EPSG:4326 reprojiziert. Fehlt nur, wenn der Punkt nicht reprojiziert werden kann: entweder ist der `srid` nicht transformierbar (kein Transformer / nicht unterstütztes Paar) oder die Reprojektion selbst schlägt fehl (Best-Effort — die Kernabfrage liefert dennoch). Gedacht zum Weiterrechnen/Speichern in nachgelagerten Services.",
            "nullable": true
          }
        },
        "required": [
          "coordinate",
          "results",
          "total_features",
          "processing_time_ms"
        ],
        "type": "object"
      },
      "QueryResponsePerSource": {
        "description": "Antwort von GET /api/v1/query/{sourceId}. Wie QueryResponse, aber OHNE den gazetteer-Block (die Einzelquellen-Abfrage reichert nicht an). Der wgs84-Block wird dennoch mitgeliefert.",
        "properties": {
          "coordinate": {
            "$ref": "#/components/schemas/Coordinate"
          },
          "processing_time_ms": {
            "description": "Gesamte Verarbeitungszeit in Millisekunden",
            "format": "int64",
            "type": "integer"
          },
          "results": {
            "description": "Ergebnisse pro Datenquelle",
            "items": {
              "$ref": "#/components/schemas/QueryResult"
            },
            "type": "array"
          },
          "total_features": {
            "description": "Gesamtanzahl der gefundenen Features",
            "type": "integer"
          },
          "wgs84": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Wgs84Coordinate"
              }
            ],
            "description": "Der Abfragepunkt in WGS84 (lon/lat); bei projiziertem `srid` reprojiziert. Wie bei /query Best-Effort: fehlt, wenn der `srid` nicht transformierbar ist (kein Transformer / nicht unterstütztes Paar) oder die Reprojektion selbst fehlschlägt (die Kernabfrage liefert dennoch).",
            "nullable": true
          }
        },
        "required": [
          "coordinate",
          "results",
          "total_features",
          "processing_time_ms"
        ],
        "type": "object"
      },
      "QueryResult": {
        "description": "Abfrageergebnis für eine einzelne Datenquelle",
        "properties": {
          "feature_count": {
            "description": "Anzahl der Features",
            "type": "integer"
          },
          "features": {
            "description": "Gefundene Features",
            "items": {
              "$ref": "#/components/schemas/Feature"
            },
            "type": "array"
          },
          "license": {
            "$ref": "#/components/schemas/License"
          },
          "query_time_ms": {
            "description": "Abfragezeit in Millisekunden",
            "format": "int64",
            "type": "integer"
          },
          "source_id": {
            "description": "ID der Datenquelle",
            "type": "string"
          },
          "source_name": {
            "description": "Name der Datenquelle",
            "type": "string"
          }
        },
        "required": [
          "source_id",
          "source_name",
          "features",
          "feature_count",
          "query_time_ms"
        ],
        "type": "object"
      },
      "ReadinessStatus": {
        "description": "Readiness-Status (GET /health/ready).",
        "properties": {
          "status": {
            "enum": [
              "ok",
              "not ready"
            ],
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "type": "object"
      },
      "Source": {
        "description": "Datenquellen-Informationen",
        "properties": {
          "id": {
            "description": "Eindeutige ID der Datenquelle",
            "type": "string"
          },
          "indexed": {
            "description": "Alle Indizes erstellt",
            "type": "boolean"
          },
          "last_queried": {
            "description": "Zeitpunkt der letzten Abfrage",
            "format": "date-time",
            "type": "string"
          },
          "layer_count": {
            "description": "Anzahl der Layer",
            "type": "integer"
          },
          "license": {
            "allOf": [
              {
                "$ref": "#/components/schemas/License"
              }
            ],
            "description": "Lizenz/Attribution der Datenquelle, sofern im Paket hinterlegt. Bei GeoPackages ist es die gpkg_metadata-Zeile mit mime_type='application/json' UND md_standard_uri='https://ortus.dev/schema/dataset-metadata.json' (JSON unter anderer URI wird ignoriert); bei Raster-Bundles der license-Block des Manifests. Fehlt, wenn das Paket keine Lizenz mitführt."
          },
          "loaded_at": {
            "description": "Zeitpunkt des Ladens",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "Anzeigename",
            "type": "string"
          },
          "path": {
            "description": "Dateipfad",
            "type": "string"
          },
          "ready": {
            "description": "Bereit für Abfragen",
            "type": "boolean"
          },
          "size": {
            "description": "Dateigröße in Bytes",
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "path",
          "size",
          "layer_count",
          "indexed",
          "ready"
        ],
        "type": "object"
      },
      "SourceList": {
        "description": "Liste von Datenquellen",
        "properties": {
          "count": {
            "description": "Anzahl der Datenquellen",
            "type": "integer"
          },
          "gazetteer": {
            "description": "Identität des geladenen Gazetteer-Pakets. Der Gazetteer wird \"außer Konkurrenz\" gelesen und ist daher keine der Quellen oben — er ist aber das größte deployte Artefakt und das, das am ehesten von der Binary abdriftet. Der Build-seitige Paket-Check vergleicht nur die Dateien auf der Build-Maschine und kann nicht sehen, was ein Server tatsächlich geladen hat. Fehlt vollständig, wenn das Feature aus ist oder das Paket älter ist als diese Felder (ein leeres Objekt wäre nicht dasselbe wie \"keine Angabe\").",
            "properties": {
              "built": {
                "description": "Erstellungsdatum des Pakets.",
                "example": "2026-08-23",
                "format": "date",
                "type": "string"
              },
              "dataset_version": {
                "description": "Version der Daten (nicht die ortus-Version), z. B. \"0.2.0\".",
                "example": "0.2.0",
                "type": "string"
              }
            },
            "type": "object"
          },
          "sources": {
            "items": {
              "$ref": "#/components/schemas/Source"
            },
            "type": "array"
          }
        },
        "required": [
          "sources",
          "count"
        ],
        "type": "object"
      },
      "Wgs84Coordinate": {
        "description": "Eine WGS84-Koordinate (EPSG:4326) als lon/lat. Explizit geografisch (nicht x/y/srid), damit nachgelagerte Services direkt damit rechnen/speichern können.",
        "properties": {
          "lat": {
            "description": "Breitengrad (WGS84).",
            "format": "double",
            "type": "number"
          },
          "lon": {
            "description": "Längengrad (WGS84).",
            "format": "double",
            "type": "number"
          }
        },
        "required": [
          "lon",
          "lat"
        ],
        "type": "object"
      }
    }
  },
  "info": {
    "contact": {
      "name": "Ortus API Support",
      "url": "https://github.com/jobrunner/ortus"
    },
    "description": "REST API für die Abfrage von Datenquellen mittels Punktkoordinaten.\n\nOrtus ermöglicht die effiziente räumliche Abfrage von Datenquellen und unterstützt\nverschiedene Koordinatensysteme (SRIDs). Die API kann mehrere Datenquellen gleichzeitig\nverwalten und abfragen.\n\n## Koordinatensysteme\n\nDie API unterstützt verschiedene Koordinatensysteme über den `srid` Parameter:\n- **4326** (WGS84): Standard GPS-Koordinaten (lon/lat)\n- **3857** (Web Mercator): Web-Mapping Standard\n- **25832/25833** (ETRS89/UTM): Deutscher/EU Standard\n- **31466/31467** (DHDN/Gauß-Krüger): Deutsches Legacy-System\n\n## Authentifizierung\n\nDerzeit ist keine Authentifizierung erforderlich.\n",
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    },
    "title": "Ortus Source Query API",
    "version": "1.0.0"
  },
  "openapi": "3.0.3",
  "paths": {
    "/gazetteer": {
      "get": {
        "description": "Löst eine Koordinate auf ihre Verwaltungshierarchie (`admin`) auf und berechnet\neine Peilung zum salientesten nahen Ort (`bearing`, z. B. \"4 km E Würzburg\").\nJeder Teil ist `null`, wenn es kein Ergebnis gibt — keine Admin-Abdeckung bzw.\nkein Anker in Reichweite. Nur verfügbar, wenn das Gazetteer-Feature aktiv ist.\nEin projizierter `srid` (z. B. 3857) wird intern nach WGS84 reprojiziert; die\nAntwort enthält die reprojizierte Koordinate im `wgs84`-Block. Nur ein `srid`,\nder sich nicht nach WGS84 transformieren lässt, wird mit 422 abgelehnt.\n",
        "operationId": "gazetteer",
        "parameters": [
          {
            "$ref": "#/components/parameters/LonParam"
          },
          {
            "$ref": "#/components/parameters/LatParam"
          },
          {
            "$ref": "#/components/parameters/XParam"
          },
          {
            "$ref": "#/components/parameters/YParam"
          },
          {
            "$ref": "#/components/parameters/SridParam"
          },
          {
            "$ref": "#/components/parameters/MgrsParam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "admin": {
                    "country_iso": "DE",
                    "hierarchy": [
                      {
                        "equivalent": "municipality",
                        "equivalent_description": "Local municipal authority (city/town/commune).",
                        "level": 8,
                        "local_term": "Kreisfreie Stadt",
                        "name": "Würzburg",
                        "name_native": "",
                        "name_source": "latin-osm"
                      },
                      {
                        "equivalent": "state",
                        "equivalent_description": "First-level subdivision (state, region, province).",
                        "level": 4,
                        "local_term": "Land",
                        "name": "Bayern",
                        "name_native": "",
                        "name_source": "latin-osm"
                      }
                    ]
                  },
                  "bearing": {
                    "azimuth": 90,
                    "class": "city",
                    "compass": "E",
                    "distance_km": 4,
                    "inside": false,
                    "label": "4 km E Würzburg",
                    "name_native": "",
                    "name_source": "latin-osm",
                    "reference": "Würzburg"
                  },
                  "coordinate": {
                    "srid": 4326,
                    "x": 9.93,
                    "y": 49.79
                  },
                  "elevation": {
                    "accuracy_basis": "GLO-30 LE90 (absolute)",
                    "accuracy_m": 4,
                    "horizontal_accuracy_m": 6,
                    "meters": 177,
                    "sea_level": false,
                    "source": {
                      "attribution": "© DLR e.V. 2010-2014 und © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved.",
                      "name": "Copernicus DEM GLO-30",
                      "url": "https://doi.org/10.5270/ESA-c5d3d65"
                    },
                    "surface_model": "DSM",
                    "vertical_datum": "EGM2008"
                  },
                  "exposure": {
                    "aspect_compass": "SE",
                    "aspect_deg": 135,
                    "flat": false,
                    "sample_spacing_m": 30,
                    "slope_deg": 12.5,
                    "slope_percent": 22.2,
                    "source": {
                      "attribution": "© DLR e.V. 2010-2014 und © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved.",
                      "name": "Copernicus DEM GLO-30",
                      "url": "https://doi.org/10.5270/ESA-c5d3d65"
                    }
                  },
                  "islands": null,
                  "license": {
                    "attribution": "© OpenStreetMap contributors (ODbL 1.0); Natural Earth (public domain); GeoNames (CC BY 4.0); NGA GNS (public domain)",
                    "name": "ODbL-1.0",
                    "url": "https://opendatacommons.org/licenses/odbl/1-0/"
                  },
                  "sources": [
                    {
                      "code": "latin-osm",
                      "long": "Taken verbatim from the OpenStreetMap name tag; already Latin script, no transliteration applied.",
                      "short": "OSM name (already Latin)",
                      "standard": ""
                    }
                  ],
                  "wgs84": {
                    "lat": 49.79,
                    "lon": 9.93
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/GazetteerResponse"
                }
              }
            },
            "description": "Erfolgreiche Abfrage"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Ungültige Parameter"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Reverse-Geocoding + Peilung",
        "tags": [
          "Gazetteer"
        ]
      }
    },
    "/health": {
      "get": {
        "description": "Gibt detaillierte Informationen über den Gesundheitszustand des Services zurück.\nEnthält Informationen über geladene Datenquellen und den Status einzelner Komponenten.\n",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "components": {
                    "storage": "ok"
                  },
                  "ready": true,
                  "sources_loaded": 3,
                  "sources_ready": 3,
                  "status": "ok"
                },
                "schema": {
                  "$ref": "#/components/schemas/HealthStatus"
                }
              }
            },
            "description": "Service ist gesund"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "components": {
                    "storage": "ok"
                  },
                  "ready": false,
                  "sources_loaded": 0,
                  "sources_ready": 0,
                  "status": "unhealthy"
                },
                "schema": {
                  "$ref": "#/components/schemas/HealthStatus"
                }
              }
            },
            "description": "Service ist nicht gesund"
          }
        },
        "servers": [
          {
            "description": "Root",
            "url": "/"
          }
        ],
        "summary": "Detaillierter Gesundheitsstatus",
        "tags": [
          "Health"
        ]
      }
    },
    "/health/live": {
      "get": {
        "description": "Einfacher Liveness-Check für Kubernetes.\nGibt 200 zurück, wenn der Service läuft, sonst 503.\n",
        "operationId": "getLiveness",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "status": "ok"
                },
                "schema": {
                  "$ref": "#/components/schemas/LivenessStatus"
                }
              }
            },
            "description": "Service ist aktiv"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "status": "unhealthy"
                },
                "schema": {
                  "$ref": "#/components/schemas/LivenessStatus"
                }
              }
            },
            "description": "Service ist nicht aktiv"
          }
        },
        "servers": [
          {
            "description": "Root",
            "url": "/"
          }
        ],
        "summary": "Kubernetes Liveness Probe",
        "tags": [
          "Health"
        ]
      }
    },
    "/health/ready": {
      "get": {
        "description": "Readiness-Check für Kubernetes.\nGibt 200 zurück, wenn der Service bereit ist, Anfragen zu verarbeiten, sonst 503.\n",
        "operationId": "getReadiness",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "status": "ok"
                },
                "schema": {
                  "$ref": "#/components/schemas/ReadinessStatus"
                }
              }
            },
            "description": "Service ist bereit"
          },
          "503": {
            "content": {
              "application/json": {
                "example": {
                  "status": "not ready"
                },
                "schema": {
                  "$ref": "#/components/schemas/ReadinessStatus"
                }
              }
            },
            "description": "Service ist nicht bereit"
          }
        },
        "servers": [
          {
            "description": "Root",
            "url": "/"
          }
        ],
        "summary": "Kubernetes Readiness Probe",
        "tags": [
          "Health"
        ]
      }
    },
    "/query": {
      "get": {
        "description": "Führt eine Punktabfrage über alle registrierten Datenquellen durch.\n\nKoordinaten können als `lon/lat` (für WGS84), als `x/y` (für andere\nKoordinatensysteme) oder als `mgrs` (Military Grid Reference System,\nsiehe dessen Parameterbeschreibung) angegeben werden.\n\nDie Punkt-in-Polygon-Prüfung ist randinklusiv (ST_Covers): ein Punkt, der\nexakt auf der Grenze eines Polygons liegt, gehört zu diesem Polygon. Liegt\nein Punkt genau auf der Grenze zwischen zwei verschiedenen Regionen, werden\nbeide zurückgegeben. Fragmente derselben Region (z. B. durch ST_Subdivide\ngekachelte Quellen) werden anhand ihrer Attribute dedupliziert, sodass eine\ngekachelte Quelle dieselben Treffer liefert wie ihre ungekachelte Vorlage.\nBei aktivem `query.with_geometry` liefert eine gekachelte Quelle das\njeweilige Teil-Polygon (Subdivision-Fragment), nicht das ursprüngliche\nGesamtpolygon; ebenso ist die zurückgegebene `id` die fid des behaltenen\nFragments (nach Deduplizierung) und entspricht nicht zwingend der\nFeature-ID einer ungekachelten Quelle — Kacheln bleibt eine optionale\nPackaging-Entscheidung.\n",
        "operationId": "queryAllSources",
        "parameters": [
          {
            "$ref": "#/components/parameters/LonParam"
          },
          {
            "$ref": "#/components/parameters/LatParam"
          },
          {
            "$ref": "#/components/parameters/XParam"
          },
          {
            "$ref": "#/components/parameters/YParam"
          },
          {
            "$ref": "#/components/parameters/SridParam"
          },
          {
            "$ref": "#/components/parameters/MgrsParam"
          },
          {
            "$ref": "#/components/parameters/PropertiesParam"
          },
          {
            "$ref": "#/components/parameters/WithGazetteerParam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "coordinate": {
                    "srid": 4326,
                    "x": 13.405,
                    "y": 52.52
                  },
                  "gazetteer": {
                    "admin": {
                      "country_iso": "DE",
                      "hierarchy": [
                        {
                          "equivalent": "state",
                          "equivalent_description": "First-level subdivision (state, region, province).",
                          "level": 4,
                          "local_term": "Land",
                          "name": "Berlin",
                          "name_native": "",
                          "name_source": "latin-osm"
                        }
                      ]
                    },
                    "bearing": {
                      "azimuth": 0,
                      "class": "city",
                      "compass": "",
                      "distance_km": 0,
                      "inside": true,
                      "label": "in Berlin",
                      "name_native": "",
                      "name_source": "latin-osm",
                      "reference": "Berlin"
                    },
                    "exposure": null,
                    "islands": null,
                    "license": {
                      "attribution": "© OpenStreetMap contributors (ODbL 1.0); Natural Earth (public domain); GeoNames (CC BY 4.0); NGA GNS (public domain)",
                      "name": "ODbL-1.0",
                      "url": "https://opendatacommons.org/licenses/odbl/1-0/"
                    },
                    "sources": [
                      {
                        "code": "latin-osm",
                        "long": "Taken verbatim from the OpenStreetMap name tag; already Latin script, no transliteration applied.",
                        "short": "OSM name (already Latin)",
                        "standard": ""
                      }
                    ]
                  },
                  "processing_time_ms": 12,
                  "results": [
                    {
                      "feature_count": 1,
                      "features": [
                        {
                          "geometry": {
                            "type": "MULTIPOLYGON",
                            "wkt": "MULTIPOLYGON(((13.3 52.5, 13.4 52.5, 13.4 52.6, 13.3 52.6, 13.3 52.5)))"
                          },
                          "id": 42,
                          "layer": "districts",
                          "properties": {
                            "name": "Mitte",
                            "population": 384172
                          }
                        }
                      ],
                      "license": {
                        "attribution": "© OpenData Berlin",
                        "name": "CC BY 4.0",
                        "url": "https://creativecommons.org/licenses/by/4.0/"
                      },
                      "query_time_ms": 5,
                      "source_id": "districts",
                      "source_name": "districts.gpkg"
                    }
                  ],
                  "total_features": 1,
                  "wgs84": {
                    "lat": 52.52,
                    "lon": 13.405
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/QueryResponse"
                }
              }
            },
            "description": "Erfolgreiche Abfrage"
          },
          "400": {
            "content": {
              "application/json": {
                "examples": {
                  "invalidLon": {
                    "summary": "Ungültige Longitude",
                    "value": {
                      "error": "Bad Request",
                      "message": "invalid lon parameter"
                    }
                  },
                  "missingCoordinates": {
                    "summary": "Fehlende Koordinaten",
                    "value": {
                      "error": "Bad Request",
                      "message": "coordinates required: use lon/lat, x/y, or mgrs"
                    }
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Ungültige Parameter"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Alle Datenquellen abfragen",
        "tags": [
          "Query"
        ]
      }
    },
    "/query/batch": {
      "post": {
        "description": "Löst viele Koordinaten in EINEM Request auf. Die Punkt-in-Polygon-Abfrage\nläuft set-based (eine Query pro Datenquelle für alle Punkte) — deutlich\neffizienter als ein Request pro Koordinate.\n\nLiefert standardmäßig ein JSON-Objekt `{results, total, processing_time_ms}`.\nMit `Accept: application/x-ndjson` wird stattdessen ein NDJSON-Stream\ngeliefert (ein Ergebnis-Objekt pro Zeile) — für sehr große Batches.\n\nJedes Ergebnis trägt die vom Client gewählte `id` (bzw. den 0-basierten\nIndex) und entspricht sonst einer Einzelpunkt-Antwort plus `wgs84` und —\nstandardmäßig (konsistent mit /query, das opt-out ist) — dem `gazetteer`-Block\n(pro Punkt, teurer); mit `with-gazetteer: false` wird er weggelassen.\nEin Fehler an einem einzelnen Punkt erscheint als `error`-Objekt in dessen\nErgebnis, ohne den ganzen Batch abzubrechen.\n",
        "operationId": "queryBatch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchQueryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchQueryResponse"
                }
              },
              "application/x-ndjson": {
                "schema": {
                  "$ref": "#/components/schemas/BatchQueryResultItem"
                }
              }
            },
            "description": "Ergebnisse (Sync-JSON oder NDJSON-Stream je nach Accept)"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Ungültiger Body / leere points / Hard-Cap überschritten"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Angeforderte Datenquelle nicht gefunden"
          },
          "413": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Batch zu groß. Entweder überschreitet ein Sync-Request das Sync-Limit (max_sync_points) — dann mit Accept application/x-ndjson streamen — oder der Request-Body überschreitet die Größenobergrenze (unabhängig von der Punktanzahl); in dem Fall weniger Punkte oder kleinere Felder pro Punkt senden (Streaming hilft hier nicht)."
          }
        },
        "summary": "Stapelabfrage vieler Koordinaten",
        "tags": [
          "Query"
        ]
      }
    },
    "/query/{sourceId}": {
      "get": {
        "description": "Führt eine Punktabfrage auf einer bestimmten Datenquelle durch.\n\nDie Punkt-in-Polygon-Prüfung ist randinklusiv (ST_Covers) und dedupliziert\nFragmente derselben Region; siehe die Beschreibung von `GET /api/v1/query`.\n",
        "operationId": "querySource",
        "parameters": [
          {
            "$ref": "#/components/parameters/SourceIdParam"
          },
          {
            "$ref": "#/components/parameters/LonParam"
          },
          {
            "$ref": "#/components/parameters/LatParam"
          },
          {
            "$ref": "#/components/parameters/XParam"
          },
          {
            "$ref": "#/components/parameters/YParam"
          },
          {
            "$ref": "#/components/parameters/SridParam"
          },
          {
            "$ref": "#/components/parameters/MgrsParam"
          },
          {
            "$ref": "#/components/parameters/PropertiesParam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QueryResponsePerSource"
                }
              }
            },
            "description": "Erfolgreiche Abfrage"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Ungültige Parameter"
          },
          "404": {
            "content": {
              "application/json": {
                "example": {
                  "error": "Not Found",
                  "message": "Source not found"
                },
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Datenquelle nicht gefunden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Spezifische Datenquelle abfragen",
        "tags": [
          "Query"
        ]
      }
    },
    "/sources": {
      "get": {
        "description": "Gibt eine Liste aller registrierten Datenquellen zurück.",
        "operationId": "listSources",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "count": 1,
                  "gazetteer": {
                    "built": "2026-08-23",
                    "dataset_version": "0.2.0"
                  },
                  "sources": [
                    {
                      "id": "districts",
                      "indexed": true,
                      "last_queried": "2024-01-15T10:35:00Z",
                      "layer_count": 2,
                      "loaded_at": "2024-01-15T10:30:00Z",
                      "name": "districts.gpkg",
                      "path": "/data/districts.gpkg",
                      "ready": true,
                      "size": 1048576
                    }
                  ]
                },
                "schema": {
                  "$ref": "#/components/schemas/SourceList"
                }
              }
            },
            "description": "Liste der Datenquellen"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Alle Datenquellen auflisten",
        "tags": [
          "Sources"
        ]
      }
    },
    "/sources/{sourceId}": {
      "get": {
        "description": "Gibt detaillierte Informationen zu einer bestimmten Datenquelle zurück.",
        "operationId": "getSource",
        "parameters": [
          {
            "$ref": "#/components/parameters/SourceIdParam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Source"
                }
              }
            },
            "description": "Datenquellen-Details"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Datenquelle nicht gefunden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Datenquellen-Details abrufen",
        "tags": [
          "Sources"
        ]
      }
    },
    "/sources/{sourceId}/layers": {
      "get": {
        "description": "Gibt alle Layer einer bestimmten Datenquelle zurück.",
        "operationId": "getSourceLayers",
        "parameters": [
          {
            "$ref": "#/components/parameters/SourceIdParam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "count": 1,
                  "layers": [
                    {
                      "description": "Administrative Bezirke",
                      "extent": {
                        "max_x": 13.761,
                        "max_y": 52.675,
                        "min_x": 13.088,
                        "min_y": 52.338
                      },
                      "feature_count": 12,
                      "geometry_column": "geom",
                      "geometry_type": "MULTIPOLYGON",
                      "has_index": true,
                      "name": "districts",
                      "srid": 4326
                    }
                  ],
                  "source_id": "districts"
                },
                "schema": {
                  "$ref": "#/components/schemas/LayerList"
                }
              }
            },
            "description": "Liste der Layer"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Datenquelle nicht gefunden"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Interner Serverfehler"
          }
        },
        "summary": "Datenquellen-Layer abrufen",
        "tags": [
          "Sources"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "API Version 1",
      "url": "/api/v1"
    },
    {
      "description": "Root (für Health-Endpoints)",
      "url": "/"
    }
  ],
  "tags": [
    {
      "description": "Räumliche Punktabfragen auf Datenquellen",
      "name": "Query"
    },
    {
      "description": "Reverse-Geocoding auf die Verwaltungseinheit und Peilung (bearing)",
      "name": "Gazetteer"
    },
    {
      "description": "Datenquellen-Verwaltung und -Information",
      "name": "Sources"
    },
    {
      "description": "Gesundheitsprüfungen und Kubernetes-Probes",
      "name": "Health"
    }
  ]
}