{
  "openapi": "3.1.1",
  "info": {
    "title": "RavingDesk Geodesic Distance & Bearing Calculator API",
    "version": "1.0.0",
    "summary": "Exact distance, bearings and midpoint between coordinates or along a route on WGS84.",
    "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://ravingdeskgeodesicdistance.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Exact distance, bearings and midpoint between coordinates or along a route on WGS84.",
        "description": "Measures the shortest path between two or more points on the WGS84 ellipsoid with Karney’s geodesic algorithm (nanometre accuracy, also for nearly antipodal points where Vincenty’s method fails): leg distances, initial and final bearings, midpoints, the total route length and how far the spherical Haversine formula is off. Coordinates can be written in decimal degrees, DMS, UTM, MGRS, plus codes or Maidenhead. It is the distance engine of RavingDesk’s Data & charts geo layer.\n\nMethodology:\n- Each leg solves the inverse geodesic problem on the chosen ellipsoid with Karney’s algorithm (6th-order series in the third flattening, Newton iteration on the azimuth, astroid start for nearly antipodal points). Accuracy is about 15 nanometres on WGS84.\n- The initial bearing is the azimuth at the start of the leg; the final bearing is the forward azimuth on arrival. Both are measured clockwise from true north (not magnetic north). The midpoint lies half the leg distance along the geodesic.\n- For comparison every leg is also computed with the Haversine formula on a sphere of radius 6,371,008.8 m (IUGG mean radius). The difference is typically up to about 0.5 % and depends on direction and latitude.\n- Coordinates may be decimal degrees, DMS or DDM with N/S/E/W, UTM/UPS, MGRS, plus codes or Maidenhead locators. MGRS, plus codes and locators stand for a square; the centre of the square is used.\n- Heights are ignored: distances are measured on the ellipsoid surface, not along the terrain.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ravingdesk_geodesic_distance",
        "x-owner-app": "ravingdesk",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RavingdeskGeodesicDistanceInput"
              },
              "examples": {
                "example1": {
                  "summary": "Brandenburg Gate (Berlin) to the Eiffel Tower (Paris)",
                  "value": {
                    "points": [
                      {
                        "label": "Brandenburg Gate",
                        "coordinate": "52.516275, 13.377704"
                      },
                      {
                        "label": "Eiffel Tower",
                        "coordinate": "48.858222, 2.2945"
                      }
                    ]
                  }
                },
                "example2": {
                  "summary": "A walk past seven Berlin landmarks",
                  "value": {
                    "points": [
                      {
                        "label": "Reichstag building",
                        "coordinate": "52.518589, 13.376083"
                      },
                      {
                        "label": "Brandenburg Gate",
                        "coordinate": "52.516275, 13.377704"
                      },
                      {
                        "label": "Potsdamer Platz",
                        "coordinate": "52.509663, 13.376051"
                      },
                      {
                        "label": "Checkpoint Charlie",
                        "coordinate": "52.507498, 13.390384"
                      },
                      {
                        "label": "Gendarmenmarkt",
                        "coordinate": "52.513611, 13.392778"
                      },
                      {
                        "label": "Berlin Cathedral",
                        "coordinate": "52.519133, 13.401078"
                      },
                      {
                        "label": "TV Tower",
                        "coordinate": "52.520815, 13.409419"
                      }
                    ]
                  }
                },
                "example3": {
                  "summary": "Vincenty’s nearly antipodal test line (d) on the International ellipsoid",
                  "value": {
                    "points": [
                      {
                        "label": "P1",
                        "coordinate": "1°00′00″N 0°E"
                      },
                      {
                        "label": "P2",
                        "coordinate": "0°59'53.83076\"S 179°17'48.02997\"E"
                      }
                    ],
                    "ellipsoid": "international1924"
                  }
                },
                "example4": {
                  "summary": "Mixed notations: MGRS, UTM and a plus code",
                  "value": {
                    "points": [
                      {
                        "label": "Victory Column (MGRS)",
                        "coordinate": "33U UU 88041 19548"
                      },
                      {
                        "label": "Brandenburg Gate (UTM)",
                        "coordinate": "33U 389918 5819699"
                      },
                      {
                        "label": "TV Tower (plus code)",
                        "coordinate": "9F4MGCC5+8Q"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RavingdeskGeodesicDistanceResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "RavingdeskGeodesicDistanceInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "points": {
            "title": "Points in order",
            "description": "One point per row, e.g. 52.5163, 13.3777 · 52°31′N 13°24′E · 33U 391779 5820072 · 33UUU9177920072 · 9F4MGCC4+22.",
            "type": "array",
            "minItems": 2,
            "maxItems": 500,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "label": {
                  "title": "Label",
                  "type": "string",
                  "maxLength": 80
                },
                "coordinate": {
                  "title": "Coordinate",
                  "type": "string",
                  "maxLength": 120
                }
              },
              "required": [
                "coordinate"
              ]
            },
            "default": [
              {
                "label": "Brandenburg Gate",
                "coordinate": "52.516275, 13.377704"
              },
              {
                "label": "Eiffel Tower",
                "coordinate": "48.858222, 2.2945"
              }
            ]
          },
          "closeLoop": {
            "title": "Return to the first point (closed loop)",
            "type": "boolean",
            "default": false
          },
          "ellipsoid": {
            "title": "Ellipsoid",
            "type": "string",
            "enum": [
              "wgs84",
              "grs80",
              "bessel1841",
              "international1924"
            ],
            "default": "wgs84"
          }
        },
        "required": []
      },
      "RavingdeskGeodesicDistanceResult": {
        "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",
            "additionalProperties": false,
            "required": [
              "ellipsoid",
              "points",
              "legs",
              "totalM",
              "totalKm",
              "totalHaversineM",
              "differenceM",
              "differencePercent",
              "directM",
              "closedLoop"
            ],
            "properties": {
              "ellipsoid": {
                "type": "string"
              },
              "points": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "label",
                    "lat",
                    "lon",
                    "format"
                  ],
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "lat": {
                      "type": "number"
                    },
                    "lon": {
                      "type": "number"
                    },
                    "format": {
                      "type": "string"
                    }
                  }
                }
              },
              "legs": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "from",
                    "to",
                    "distanceM",
                    "haversineM",
                    "initialBearingDeg",
                    "finalBearingDeg",
                    "midpoint",
                    "cumulativeM"
                  ],
                  "properties": {
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "distanceM": {
                      "type": "number"
                    },
                    "haversineM": {
                      "type": "number"
                    },
                    "initialBearingDeg": {
                      "type": "number"
                    },
                    "finalBearingDeg": {
                      "type": "number"
                    },
                    "midpoint": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "lat",
                        "lon"
                      ],
                      "properties": {
                        "lat": {
                          "type": "number"
                        },
                        "lon": {
                          "type": "number"
                        }
                      }
                    },
                    "cumulativeM": {
                      "type": "number"
                    }
                  }
                }
              },
              "totalM": {
                "type": "number",
                "description": "Geodesic length of the route in metres."
              },
              "totalKm": {
                "type": "number"
              },
              "totalHaversineM": {
                "type": "number",
                "description": "The same route on a sphere of radius 6,371,008.8 m."
              },
              "differenceM": {
                "type": "number",
                "description": "Haversine minus geodesic, metres."
              },
              "differencePercent": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "directM": {
                "type": "number",
                "description": "Geodesic distance from the first to the last point."
              },
              "closedLoop": {
                "type": "boolean"
              }
            }
          },
          "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"
              }
            }
          }
        }
      }
    }
  }
}