{
  "openapi": "3.1.1",
  "info": {
    "title": "GISela Buffer API",
    "version": "1.0.0",
    "summary": "Buffer zones around points, lines or polygons at a distance in metres, merged or one per feature.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://giselabuffer.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Buffer zones around points, lines or polygons at a distance in metres, merged or one per feature.",
        "description": "Creates buffer polygons at a true distance in metres around every feature of GeoJSON, KML, GPX, CSV or shapefile data: circles around points, corridors around lines, grown or shrunk polygons. Overlapping zones can be merged into one area. Returns GeoJSON, the ellipsoidal area of each zone and a map preview.\n\nMethodology:\n- Each feature (or all features when merging) is projected to a local Transverse Mercator plane centred on the data, so distances are in metres with a scale error below 0.03 % within 250 km of the centre.\n- Points become circles; every line segment and polygon edge becomes a stadium (two half circles joined by straight sides); the stadiums are merged with GISela’s polygon overlay (exact edge splitting on a snapped grid, even-odd classification). A positive polygon buffer is the polygon united with its edge band, a negative one the polygon minus the band.\n- Results are converted back to WGS 84. Areas and perimeters are ellipsoidal (Karney’s geodesic polygon algorithm on WGS 84).\n\nCircles are polygons with the chosen number of segments, so the area is slightly smaller than the true disc (0.6 % at 32 segments).",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "gisela_buffer",
        "x-owner-app": "gisela",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GiselaBufferInput"
              },
              "examples": {
                "example1": {
                  "summary": "300 m around seven tram stops, merged",
                  "value": {
                    "data": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[7.992,47.1215]},\"properties\":{\"name\":\"Weiher\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[7.999,47.126]},\"properties\":{\"name\":\"Altstadt\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[8.006,47.129]},\"properties\":{\"name\":\"Marktplatz\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[8.012,47.1335]},\"properties\":{\"name\":\"Bahnhof\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[8.018,47.137]},\"properties\":{\"name\":\"Nordring\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[8.025,47.14]},\"properties\":{\"name\":\"Rebhalde\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[8.031,47.1405]},\"properties\":{\"name\":\"Endstation Ost\"}}]}",
                    "distance": 300,
                    "dissolve": true
                  }
                },
                "example2": {
                  "summary": "150 m corridor along the tram line",
                  "value": {
                    "data": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[7.992,47.1215],[7.999,47.126],[8.006,47.129],[8.012,47.1335],[8.018,47.137],[8.025,47.14],[8.031,47.1405]]},\"properties\":{\"name\":\"Line 1\"}}]}",
                    "distance": 150
                  }
                },
                "example3": {
                  "summary": "District polygons shrunk by 100 m",
                  "value": {
                    "data": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[7.99,47.12],[8.004,47.1205],[8.0052,47.1303],[7.9891,47.131],[7.99,47.12]]]},\"properties\":{\"name\":\"Altstadt\",\"population\":4820,\"households\":2410,\"zone\":\"mixed\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[8.004,47.1205],[8.019,47.1205],[8.0181,47.1303],[8.0052,47.1303],[8.004,47.1205]]]},\"properties\":{\"name\":\"Seefeld\",\"population\":3150,\"households\":1490,\"zone\":\"residential\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[8.019,47.1205],[8.033,47.12],[8.0342,47.131],[8.0181,47.1303],[8.019,47.1205]]]},\"properties\":{\"name\":\"Moosmatt\",\"population\":2280,\"households\":1010,\"zone\":\"residential\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[7.9891,47.131],[8.0052,47.1303],[8.004,47.1425],[7.99,47.142],[7.9891,47.131]]]},\"properties\":{\"name\":\"Bahnhof\",\"population\":3900,\"households\":2050,\"zone\":\"mixed\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[8.0052,47.1303],[8.0181,47.1303],[8.019,47.1425],[8.004,47.1425],[8.0052,47.1303]]]},\"properties\":{\"name\":\"Industrie Nord\",\"population\":640,\"households\":210,\"zone\":\"industrial\"}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[8.0181,47.1303],[8.0342,47.131],[8.033,47.142],[8.019,47.1425],[8.0181,47.1303]]]},\"properties\":{\"name\":\"Rebhalde\",\"population\":1720,\"households\":760,\"zone\":\"residential\"}}]}",
                    "distance": -100,
                    "segments": 16
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GiselaBufferResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "GiselaBufferInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "data": {
            "title": "Data",
            "description": "GeoJSON, TopoJSON, KML, GPX, CSV (lat/lon, x/y or a WKT column) or WKT lines. Or upload a file below.",
            "type": "string",
            "maxLength": 5000000,
            "default": ""
          },
          "file": {
            "title": "File (optional)",
            "description": "Used instead of the pasted text. Shapefiles: put .shp, .dbf and .prj into one ZIP. Upload as {filename, contentType?, contentBase64}. Max 19531 KiB. Accepts .geojson,.json,.topojson,.kml,.kmz,.gpx,.csv,.txt,.wkt,.zip.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "from": {
            "title": "Coordinate system of the data",
            "description": "“auto” reads it from the file (.prj, GeoJSON crs); otherwise an EPSG code such as 2056, 25832 or 27700. Without information the data is read as WGS 84 longitude/latitude.",
            "type": "string",
            "maxLength": 20,
            "default": "auto"
          },
          "distance": {
            "title": "Distance (metres)",
            "description": "Negative values shrink polygons.",
            "type": "number",
            "minimum": -100000,
            "maximum": 1000000,
            "default": 300,
            "x-unit": "m"
          },
          "segments": {
            "title": "Segments per circle",
            "description": "More segments = rounder curves, bigger files.",
            "type": "integer",
            "minimum": 8,
            "maximum": 128,
            "default": 32
          },
          "dissolve": {
            "title": "Merge overlapping zones into one",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "GiselaBufferResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object"
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}