API and protocol reference

Protocol tractrix/1Engine 0.2.0Beta

One JSON contract connects every Tractrix client to the OpenAirside engine. Learn it once and use it from the command line, from Python, or over HTTPS with Tractrix Cloud.

On this page

Overview

Table 1 How each client reaches the engine.
ClientTransport
Tractrix CloudPython import (tractrix_engine.run) behind POST /api/v1/simulate
Tractrix for AutoCAD / RevitLocal: tractrix-engine run, JSON on stdin → JSON on stdout, offline. Cloud: HTTPS POST /api/v1/simulate with a bearer API token.
Tractrix for QGISUses the engine in-process through the Processing algorithms (not this protocol)
Scripts / CItractrix-engine run request.json, or import tractrix_engine

Conventions

  • Coordinates are planar metres in the drawing's own coordinate system: CAD world units in metres, or a projected CRS.
  • Headings are degrees clockwise from north (+Y), matching the QGIS tools.
  • Geometry uses GeoJSON geometry objects (Point, LineString, Polygon, MultiPolygon) without a CRS member.
  • Clients must ignore unknown fields.

Request

{
  "protocol": "tractrix/1",
  "task": "aircraft_path",
  "params": { "...": "task-specific, see below" },
  "options": { "footprint_spacing_m": 10, "layers": ["envelopes", "tracks", "footprints", "issues"] }
}
  • options.layers limits which layers are returned. The default is all except poses.
  • options.footprint_spacing_m defaults to the QGIS tool's value for the task: 10 m for aircraft_path and stand, 5 m for pushback, 3 m for vehicle_path; minimum 0.5 m.
  • options.simplify_m (default 0.01 m) simplifies returned geometry, preserving topology; set 0 to turn it off. Summaries and checks always use the full geometry.
  • An id on the request is echoed in the response.

Response

{
  "protocol": "tractrix/1",
  "task": "aircraft_path",
  "ok": true,
  "engine": { "name": "OpenAirside", "version": "0.2.0" },
  "summary": { "runs": { "A320": { "max_steer_deg": 41.7, "...": "..." } } },
  "notes": ["estimated geometry: nose/tail stations ..."],
  "layers": {
    "envelopes":  [ { "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": [] },
                      "properties": { "run": "A320", "element": "swept_envelope", "unit": "A320",
                                      "area_m2": 0.0, "clearance_m": 0.0 } } ],
    "tracks":     [ { "properties": { "run": "A320", "element": "nose_gear", "unit": "A320", "length_m": 0.0 } } ],
    "footprints": [ { "properties": { "run": "A320", "unit": "A320", "step": 0, "s_m": 0.0, "t_s": 0.0,
                                      "heading": 0.0, "steer_deg": 0.0 } } ],
    "poses":      [ { "properties": { "step": 0, "s_m": 0.0, "t_s": 0.0, "heading": 0.0, "steer_deg": 0.0,
                                      "steer_limit": 70.0, "steer_ok": true } } ],
    "issues":     [ { "properties": { "run": "A320", "kind": "edge_margin", "value": 1.2, "limit": 3.0,
                                      "s_m": 120.5, "note": "" } } ]
  }
}

Responses also carry elapsed_ms. Every summary.runs[id] has notes. The value 41.7 above is illustrative.

Errors and exit codes

{ "protocol": "tractrix/1", "ok": false,
  "error": { "code": "unknown_aircraft", "message": "..." } }
Table 2 Error codes.
codeMeaningCLI exitHTTP
bad_requestInvalid JSON or parameters, or a guard-rail limit exceeded2400 / 413
unknown_aircraftNo aircraft with that id or ICAO type2422
unknown_vehicleNo vehicle with that id2422
not_implementedReserved; not returned in 0.2.02—
engine_errorUnexpected failure inside the engine1500
plan_limitCloud only: the organisation's plan limit is reached—402
busyCloud only: all simulation slots are in use; retry after the Retry-After seconds—503
timeoutCloud only: the simulation exceeded the time-out—504

The CLI always writes a JSON document to stdout, even on error; logs go to stderr.

Element names

properties.element names are shared by every client, so they map to CAD layers and styles: swept_envelope, clearance_envelope, main_gear_swath, nose_gear_swath, engine_swath, tug_envelope, nose_gear, cockpit, main_gear_left, main_gear_right, wingtip_left, wingtip_right, tail, engine_N, stand_clearance, stop_bar, required_pavement, fillet, jet_blast_<v>kmh, intake_hazard. Engine 0.2.0 adds parked_footprint, nominal_taxiway, nose, main_gear_centre, tailplane tips, tug and vehicle axle tracks, and separation. Map unknown elements to a default layer.

Issue kinds

steering_limit, edge_margin, obstacle_conflict (with conflict: contact or clearance), stand_conflict (with conflict: stand_clearance or taxi_in_clearance), separation and jackknife. Each issue has value, limit and, where it applies, s_m — the distance along the path.

Tasks

library

params: {"kind": "aircraft" | "vehicles", "query": "optional text filter"}. summary.items holds the library records. The query is a case-insensitive substring of id, ICAO type, name and manufacturer (aircraft) or id, name and category (vehicles).

aircraft_path

Forward taxi and group paths.

paramTypeDefault
aircraftICAO type id (e.g. "A320", "B77W") or a list of ids (group path)required
path[[x, y], ...] in the direction of travel, or a GeoJSON LineStringrequired
tracking"nose_gear" | "cockpit" | "custom""cockpit"
tracking_distance_mnumber, for custom only—
speed_kmhnumber15
max_steer_degoverride of the library limitlibrary
standard"ICAO" | "FAA" | "EASA""ICAO"
context"taxiway" | "taxilane" | "stand""taxiway"
clearance_moverride, ≥ 0from standard
pavementlist of Polygon coordinate arrays (edge-margin check)none
obstacleslist of Polygon coordinate arraysnone

summary.runs[id]: max_steer_deg, steer_limit_deg, steer_exceedances, min_edge_margin_m, edge_margin_required_m, edge_breaches, obstacle_conflicts, clearance_m, swept_area_m2, path_length_m, duration_s. Run ids are aircraft ids; repeats become A320#2.

pushback

Pushback and towing. aircraft; path (main-gear path); mode: "pushback" | "tow"; tug: vehicle id (default TUG_TOWBARLESS_GENERIC); towbar: bool (default false); towbar_length_m (default: the tug's, or 6 m); tow_limit_deg (default 60; the QGIS tool defaults to 90); speed_kmh (default 5); standard; context (default "stand"); clearance_m; pavement; obstacles.

summary.runs[id]: max_nose_wheel_deg, tow_limit_deg, exceedances, plus the aircraft_path fields.

turning

Turning radii and the 180° turn. aircraft (id or list); steer_deg (0 = maximum); optional location [x, y] and heading (degrees) to draw the turn.

summary.runs[id]: R_main_gear_centre, R1_inner_main_gear_outer_edge, R2_outer_main_gear_outer_edge, R3_nose_gear, R4_wingtip, R5_nose, R6_tail, min_pavement_width_180, steer_deg. With location, layers.envelopes holds the 180° swept area.

jet_blast

Velocity contours. aircraft; positions: [{"x": 0, "y": 0, "heading": 90}] (items may also be [x, y, heading] and may name their own aircraft); reference: "main_gear" | "nose" (default "main_gear"); thrust: "idle" | "breakaway" | "takeoff" or a list (default "breakaway"); velocities_kmh (default [56, 35]); intake: bool (default true); dissolve: bool (default false).

Features in layers.envelopes have element = jet_blast_<v>kmh or intake_hazard, with thrust, velocity_kmh and length_m. summary.runs[id].contours[] gives length_m from the nozzle and behind_tail_m, the ICAO datum.

vehicle_path

GSE and trains. vehicle (id); path; speed_kmh (default 15); clearance_m (default 0.5); pavement; obstacles. summary.runs[id]: max_steer_deg, steer_limit_deg, steer_exceedances, max_articulation_deg, jackknife.

stand (v1.1)

Stand entry and clearance. aircraft; lead_in: [[x, y], ...] ending at the stop position; standard; neighbours: parked footprints of other stands, for example the parked_footprint envelopes of earlier stand responses. Returns the parked footprint, entry sweep, stand clearance zone, stop bar and conflicts. One stand per request.

fillet (v1.1)

Required pavement. aircraft; path (taxiway centreline); standard; pavement. Returns required_pavement and fillet polygons and summary.max_track_in_m.

separation (engine extra)

params.envelopes: GeoJSON Features with properties.run, for example layers.envelopes concatenated from earlier responses; required_m (default 7.5); elements filters which envelopes are compared (default swept_envelope and parked_footprint). Output: summary.pairs, separation tracks and separation issues.

Guard rails

The engine rejects with bad_request paths longer than 10 km or with more than 100,000 vertices, requests with more than 20 aircraft or 2,000 jet-blast positions, and inputs of more than 5,000 polygons or 200,000 vertices in total. Tractrix Cloud applies its own, lower limits.

Command line: tractrix-engine

Install with pip install ./engine from a checkout (Python 3.9 or later; shapely 2 and numpy are installed with it). The CAD add-ins bundle a single-file executable built with engine/build_binary.py.

tractrix-engine run [FILE|-]                            request from FILE or stdin (default) → response on stdout
tractrix-engine library aircraft|vehicles [--query T]    library records
tractrix-engine version                                  name, version, protocol, tasks
tractrix-engine schema [--response]                      JSON Schema (draft 2020-12) of requests / responses
options: --pretty (indent output), -v (log to stderr)
  • stdout carries only JSON: one document, newline-terminated, with non-ASCII escaped. stderr carries logs.
  • Input is UTF-8; a byte-order mark is accepted.
  • Exit codes: 0 success; 2 request error (also invalid JSON, a missing file, bad arguments); 1 engine error.
  • python -m tractrix_engine is equivalent to tractrix-engine.
# run an example request
tractrix-engine run engine/examples/aircraft_path.json --pretty

# pipe a request through stdin
echo '{"protocol":"tractrix/1","task":"turning","params":{"aircraft":"A320"}}' \
  | tractrix-engine run --pretty

# find library ids
tractrix-engine library aircraft --query A35

Python

import tractrix_engine as te

resp = te.run({"protocol": "tractrix/1", "task": "aircraft_path",
               "params": {"aircraft": ["A320", "B738"],
                          "path": [[0, 0], [200, 0], [245, 45]]}})
if resp["ok"]:
    print(resp["summary"]["runs"]["A320"]["max_steer_deg"])

te.library("aircraft", "A32")              # list of library records
te.request_schema(), te.response_schema()  # JSON Schemas

run() never raises for bad input; it returns the error envelope. Calls are independent and thread-safe.

Tractrix Cloud API

Preview

The Cloud API is part of the Tractrix Cloud preview. The base URL is given with your invitation; https://cloud.example below is a placeholder.

Authentication

Machine clients use an organisation API token (prefix tx_) created under Organisation → API tokens. Send it as a bearer token. The token identifies the organisation, so no other header is needed. Signed-in browser sessions instead send X-Tractrix-Org with the organisation id.

export TRACTRIX_TOKEN=tx_…            # shown once when created
curl -s https://cloud.example/api/v1/whoami \
  -H "Authorization: Bearer $TRACTRIX_TOKEN"

POST /api/v1/simulate

Runs a tractrix/1 request synchronously and returns the protocol response. Viewers can call the library task only; running a simulation requires the member role. Each simulation counts towards the organisation's monthly runs. The response carries an X-Tractrix-Duration-Ms header.

curl -s https://cloud.example/api/v1/simulate \
  -H "Authorization: Bearer $TRACTRIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "protocol": "tractrix/1",
        "task": "aircraft_path",
        "params": { "aircraft": "A320",
                    "path": [[0, 0], [60, 0], [100, 40]],
                    "speed_kmh": 15 },
        "options": { "footprint_spacing_m": 10 }
      }'
# jet blast at a stand, two thrust levels
curl -s https://cloud.example/api/v1/simulate \
  -H "Authorization: Bearer $TRACTRIX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"protocol":"tractrix/1","task":"jet_blast",
       "params":{"aircraft":"B77W","positions":[{"x":0,"y":0,"heading":90}],
                 "thrust":["idle","breakaway"],"velocities_kmh":[56,35]}}'

GET /api/v1/library/{kind}

Library records for aircraft or vehicles, optionally filtered with ?q=.

curl -s "https://cloud.example/api/v1/library/aircraft?q=A35" \
  -H "Authorization: Bearer $TRACTRIX_TOKEN"

Projects, drawings and runs

The web application uses the same API for stored work, under /api/orgs/{org_id}/projects:

Table 3 Project endpoints (relative to /api/orgs/{org_id}/projects).
Method and pathPurpose
GET / · POST /List or create projects
GET · PATCH · DELETE /{project_id}Read, update or delete a project with its drawings and runs
POST /{project_id}/drawingsUpload a drawing (.geojson / .json or .dxf, planar metres)
GET /{project_id}/drawings/{drawing_id}Drawing metadata and normalised GeoJSON
POST /{project_id}/runsQueue a simulation run (tractrix/1 request); returns 202
GET /{project_id}/runs/{run_id}Run status and summary
GET /{project_id}/runs/{run_id}/resultFull protocol response of a finished run
GET /{project_id}/runs/{run_id}/export/{fmt}Export: geojson, dxf, csv or html (report)

Cloud limits

In the preview: simulation requests up to 4 MB, 20,000 points per path, 12 aircraft or vehicles per request, 30 s per synchronous simulation, drawing uploads up to 20 MB (and the plan's drawing size limit). Requests beyond a limit receive a clear error; plan limits return HTTP 402.

Versioning

Additive fields do not change the protocol version. Renaming or removing a field requires tractrix/2. Clients must ignore unknown fields. JSON Schemas for requests and responses are published in engine/schema/ and printed by tractrix-engine schema.