These command-line tools help you turn a map or drawing of a racing circuit into a TORCS track, and improve tracks you already have. They can follow the circuit's centerline, convert it into TORCS straights and bends, make the lap join up, add curbs and pits, and apply hills and banking from elevation data.
The Python tools produce track XML, intermediate data, and preview images. TORCS's separate trackgen program builds the 3D track model; accc can combine additional visual layers such as a raceline. You still inspect the previews and test the result in TORCS. These are authoring tools run from a terminal, rather than an interactive track editor.
The usual journey is map or drawing → track layout → curbs and pits → elevation → 3D generation → in-game check. You can also use individual tools on an existing track, for example to generate its menu map.
For the underlying XML format and how TORCS represents roads, sides, surfaces, and terrain, see the Track Manual.
Contents
Station is a position measured in meters along the track's centerline, starting from a chosen zero point and following the loop's direction. Station 800 means “800 meters along the centerline,” not 800 meters in a straight line from the start. It is a distance along the layout in plan view, rather than a distance measured over hills or along a driver's racing line.
Source stations and XML stations can differ. A source map may start elsewhere, run in the opposite direction, or have a slightly different lap length from the fitted TORCS track. XML station zero is the beginning of the first track segment, at the simulated start/finish line; source station zero is the start chosen for the source loop. A station mapping matches positions between the two, so a hill or banking zone lands at the intended place. Keep that mapping with imported profiles, especially after moving the start line or adding pits.
| Term | Meaning here |
|---|---|
| Centerline / mainline | The line around the middle of the racing circuit, excluding the pit lane. A polyline stores a line as an ordered list of points. |
| Segment | One straight or bend in the TORCS definition: str = straight, lft = left turn, rgt = right turn. Left and right follow the driving direction. |
| Closure / seam | Making the end of the lap meet the beginning at the same position and direction. The seam is that join. |
| Plan view / heading / pose | The horizontal layout seen from above / the direction a segment faces / its position and direction together. |
| Elevation / banking | Height along the track / the road's sideways tilt. An elevation profile describes height at successive stations. |
| Pit host / taper | The main-track segment or sequence beside which pits are placed / a gradual change in side width at an entry or exit. |
| Cache / overlay | Saved source or intermediate data for reuse / a preview drawing showing layouts together so differences are visible. SVG previews open in a web browser. |
| Staging / promotion / live XML | Working on a candidate file / making the checked candidate the active track / the XML currently used by that track. |
| Gate / residual / dry run | A check against an allowed limit / the remaining mismatch, such as a small gap at the seam / a check that reports results without applying edits. |
| Raster / heightmap / relief | A grid of values, such as image pixels or elevations / an image encoding terrain heights / lines that guide terrain generation. |
OpenStreetMap (OSM) is a public map database. An OSM way is an ordered line of map points; a relation groups ways, sometimes covering several circuit variants. OpenHistoricalMap (OHM) provides historical map data. Overpass is the query service used to retrieve these map objects.
A DEM (digital elevation model) stores ground heights. A DTM (digital terrain model) describes bare-earth terrain rather than tree tops or roofs; LiDAR is laser-survey data from which such models can be made. A CRS (coordinate reference system) tells the tools how map coordinates locate points on Earth. Georeferencing places a drawing in those real-world coordinates. A bbox is the rectangular bounding box enclosing an area. Vertical units and the height reference (vertical datum) must be checked separately from the horizontal CRS.
Choose one of Conda, venv, or uv below to install the package dependencies in an isolated environment. The package requires Python 3.12 or newer; the supplied setup uses 3.12. You also need a working TORCS/trackgen installation for layout closure, model generation, and in-game checks; installing this Python package does not build those programs.
Run the setup commands below from torcs/torcs/src/tools/tracktools/, where environment.yml and pyproject.toml live. For authoring, switch to the TORCS project root as described in Quick Workflow.
The examples use PowerShell and the Windows runtime/ tree. A trailing backtick continues a command on the next line; in Bash, use a backslash instead, or put the command on one line. Replace placeholders such as <name> and <chosen-layout> with your values, without the angle brackets. The .ps1 setup scripts and .exe examples are Windows-specific; use the corresponding executable and data paths for your installation on other platforms.
If the environment already exists, update it after dependency changes:
conda run executes commands inside an existing environment; it does not install missing dependencies by itself.
The console commands are declared in pyproject.toml, so Conda, venv, and uv should all run the same entry points after the environment has been installed or refreshed. If a command is missing, update/reinstall the editable environment before falling back to python -m torcs_track_tools.cli.<module> --help.
The authoring examples below use conda run -n torcs-tracktools. With venv, activate the installed environment and omit that prefix, or use the full path to its executable. With uv, run from the TORCS root using uv run --project src/tools/tracktools <command> ... so uv finds the package while track paths remain relative to the TORCS root.
Run commands from the TORCS project root, torcs/torcs/, unless noted otherwise. The project root is the directory containing TORCS.sln; if your checkout has an outer workspace parent, that parent is not the TORCS project root. If you run examples that use runtime/tracks/... from the outer folder, they create or read a wrong top-level runtime/... tree.
Path rules:
If direction is wrong in game, rebuild with --reverse-input instead of editing XML by hand.
The default Y closure gate is 0.001m because trackgen commonly reports a sub-millimeter quantized residual such as 0.000977; keep X and angle gates strict unless there is a documented reason to widen them. The legacy closer remains available for comparison, but the recommended workflow passes --closure-solver geometric explicitly.
Normal cached rebuild:
Reverse race direction from the source order:
Legacy polyline fallback for comparison or recovery:
For OpenHistoricalMap sources, use OHM's Overpass interpreter with --overpass-url. Keep OHM caches distinct from regular OSM caches, for example centerline-ohm.json:
If a coarse historic/OHM trace has good source coverage but a large seam heading mismatch, try a detail-preserving rebuild before changing low-level fitter settings:
Only widen hidden baseline gates such as --max-baseline-ang for diagnosis or confirmed coarse-source recovery, and still accept the result only when source/closure-report-mainline.json has "ok": true, side-cleanup reports have no unresolved issues, and trackgen -z passes.
The normal authoring pipeline keeps each concern in a separate script so failed stages are easy to diagnose:
You do not need to call every script yourself: torcs-rebuild-mainline runs the core layout stages together. The DP-biarc drafter uses dynamic programming to choose a sequence of straight pieces and paired circular arcs that approximates the source. The geometric closer's terminal lever is a small adjustable straight at the end of the lap used to remove the remaining longitudinal gap.
| Stage | Script | Purpose |
|---|---|---|
| OSM relation discovery | torcs-fetch-track-layout | Lists relation layout candidates and exports the chosen racing loop. |
| Manual OSM way source | torcs-fetch-centerline | Fetches explicit way IDs when relation roles are noisy or ambiguous. |
| SVG source extraction | torcs-svg-to-centerline | Selects and scales a closed SVG path, polyline, or polygon into centerline JSON. |
| Raster source extraction | torcs-image-to-centerline | Extracts and scales one closed loop from a map image. |
| Source georeferencing | torcs-georeference-centerline | Aligns a local SVG/raster centerline to a geographic reference or distributed control points. |
| Segment drafting | torcs-draft-segments | DP-biarc drafter that approximates source points with TORCS str/lft/rgt segments. |
| Legacy segment fitting | torcs-polyline-to-torcs | Older sequential polyline fitter; keep as an explicit fallback with torcs-rebuild-mainline --segment-fitter polyline. |
| XML assembly | torcs-apply-segments | Replaces only Main Track/Track Segments in the target XML/template. |
| No-pit rebuild orchestration | torcs-rebuild-mainline | Runs DP drafting, XML assembly, candidate selection, and strict closure. |
| Tight-turn side cleanup | torcs-adjust-side-clearance | Reduces effective inner side widths where the side/border offset would cross a turn center; torcs-rebuild-mainline runs this by default. |
| Distant side-overlap cleanup | torcs-adjust-side-overlap | Reduces and tapers side widths where distant side envelopes overlap across narrow infield gaps; torcs-rebuild-mainline runs this by default. |
| Curb placement | torcs-place-curbs | Flattens global borders, adds inside curbs to tight/medium turns, and can split segments for raceline-guided outside curbs without touching pit zones. |
| DEM elevation fetch | torcs-fetch-elevation | Samples an OSM lat/lon centerline and offset terrain corridors into an elevation cache for road and relief generation. |
| Automatic raster DEM import | torcs-auto-elevation | Searches a provider tile API, downloads/caches matching DTM tiles for the wider landscape bbox, imports a road profile, and can optionally apply/render it. |
| Raster preparation | torcs-prepare-raster | Inspects local raster tiles, mosaics them, and writes a meter-valued GeoTIFF with optional vertical feet-to-meters scaling. |
| Raster DEM profile import | torcs-import-elevation-raster | Samples local GeoTIFF or ESRI ASCII DEM data at the accepted XML centerline after spatially fitting it to the source loop. |
| Raster road-surface import | torcs-import-road-surface-raster | Samples a high-resolution raster across the real source road width and derives both center elevation and banking. |
| Manual banking profile import | torcs-import-banking-zones | Converts source-station banking zone descriptions into a road-surface profile consumed by torcs-apply-elevation --apply-banking. |
| Road-surface profile composition | torcs-compose-road-surface-profile | Combines compatible elevation and banking profiles into one canonical road-surface profile. |
| Elevation and terrain | torcs-apply-elevation | Applies a smoothed station/elevation profile after pits, writes spline z/tangent attributes, and can generate a trackgen terrain heightmap or relief .ac without adding plan-view segments. |
| Elevation difference SVG | torcs-render-elevation-diff-svg | Renders a plan-view DEM-vs-track SVG with the real/source outline, TORCS outline, and color-coded height differences. |
| Road-surface difference SVG | torcs-render-road-surface-diff-svg | Renders a profile-only center/edge/banking diagnostic for applied road-surface profiles. |
| Terrain heightmap difference PNG | torcs-render-terrain-elevation-diff-png | Renders a whole-map terrain heightmap-vs-raster PNG using trackgen's PNG row order and blue-white-red height differences. |
| Track menu map | torcs-render-track-map | Renders the final XML driving loop as a transparent TORCS menu PNG and optional review SVG with start and direction markers. |
| Batch track menu maps | torcs-render-track-maps | Creates missing and overwrites existing menu PNGs for every track beneath a category/track directory tree. |
| Labeled segment SVG | torcs-render-labeled-segments-svg | Renders segment types, labels, direct curb markers, and hover details for layout review. |
| Legacy closure solver | torcs-close-loop | Uses trackgen -z and locked control segments to close a track. |
| Geometric closure solver | torcs-close-loop2 | Terminal-lever closer used by torcs-rebuild-mainline --closure-solver geometric. |
| Pit integration | torcs-integrate-pitlane | Adds side pits, preserves a no-pit backup, and stages for post-pit closure. |
| Overlay diagnostics | torcs-compare-overlay | Compares source vs generated XML path with metrics and SVG. Use --align-closed to match different loop start positions/directions without scaling. |
For normal layout work, prefer the source commands torcs-fetch-track-layout, torcs-svg-to-centerline, or torcs-image-to-centerline, followed by torcs-georeference-centerline when a local drawing needs geographic placement, then torcs-rebuild-mainline and torcs-integrate-pitlane. Elevation workflows use the DEM import/apply commands directly after layout acceptance. Direct closure and side-cleanup scripts are primarily diagnostics. The geometric solver uses its terminal lever for final X correction; use --segment-fitter polyline only to compare against or recover with the legacy fitter. Use --no-adjust-side-clearance or --no-adjust-side-overlap only when diagnosing the raw closed XML before side-width cleanup.
torcs-render-labeled-segments-svg uses a uniquely dominant numbered source-name family for compact automatic labels, keeps generated pit or closure names distinct, and falls back to names when compact labels would collide. Use --label-mode index for 1-based XML indices or --label-mode name for full names. --zoom-labels filters automatic source labels or index-mode indices; it is intentionally unavailable in full-name mode. --label-font-size accepts a positive finite SVG pixel size.
torcs-adjust-side-clearance checks tight turns using the effective side width on each segment, including carried state and explicit width, start width, and end width overrides. This matters after pit integration because a widened pit entry or exit corridor can extend through a nearby turn even when the global default side width is safe. The report lists all pit/wall-protected segments in protected_segments, and the narrower hard_protected_segments set that the tool will not rewrite while tapering. Pit main/host/wall sections stay hard-protected and their anomalies are report-only, but widened pit-entry or pit-exit approach corridors are still checked and can be corrected.
Use a dry-run report to validate a final pitted XML without mutating it:
Accept only when the final report has "anomaly_count": 0. If the dry-run reports anomalies, run the tool without --dry-run on the staged XML, then rerun side-clearance dry-run, side-overlap dry-run, trackgen -z, and geometry generation before promotion.
torcs-adjust-side-overlap catches a different problem than tight-turn side clearance: distant side envelopes crossing across narrow infield gaps. torcs-rebuild-mainline runs it after torcs-adjust-side-clearance and before optional curbs. Non-dry runs fail if final overlap samples remain, so source/side-overlap-report.json should have "status": "ok" and "final_overlap_count": 0 before promotion. If the report still fails, inspect the reported segment pairs and fix the layout or side widths explicitly instead of hiding the stage with --no-adjust-side-overlap.
torcs-place-curbs has two placement passes. The inside pass curbs qualifying tight/medium turn segments directly. The outside pass computes a K1999-style raceline, finds near-outside-edge approach/exit zones, snaps to existing segment boundaries when they almost fit, and splits only the remaining straight or constant-radius turn segments that need shorter curb runs.
Useful defaults are intentionally conservative and overridable:
For manual tuning on an already staged XML, run the curb tool directly:
Use --placement inside, --placement outside, or --placement both to isolate a pass. Outside curb run shaping is controlled by --outside-max-run-m, --outside-min-run-m, --outside-merge-gap-m, --split-boundary-tolerance-m, --segment-fit-ratio, and --min-split-piece-m. The outside pass skips protected pit corridors, wall borders, and variable-radius segments with end radius instead of splitting them. If curbs are placed before pits, choose pit hosts from the current curbed XML; outside splitting can rename an old host into -pre, -curb, and -post pieces. On an already accepted pitted layout, if outside curb splits disturb closure, rerun the outside pass with --segment-fit-ratio 0 to assign whole-segment outside curbs without changing geometry, then revalidate with trackgen -z, side-clearance dry-run, side-overlap dry-run, and regenerated assets.
TORCS side pits are attached beside a sufficiently straight part of the main track, called the pit host. Choose the host and side first; the tool then creates the pit area and its entry/exit width transitions. The Real Pitlane Workflow covers placement from map data and manual host selection.
--host-mode straighten can include short straight connector fragments between the requested host chain and the surrounding connector arcs; the refitted window is still a turn/straight/turn sequence.
Use --host-mode straighten when the intended manual pit host is a mostly straight chain but contains small fitted turns that make the default pose-preserving almost-straight replacement fail. This mode requires --host-start-segment and --host-end-segment; it replaces the selected chain with a true straight pit host and refits the adjacent connector turns so the overall entry/exit pose remains aligned. Add --pit-main-align end when the pit boxes should end at the end of the selected host; the default entry taper is then limited to the spare host length before pit_main_a instead of extending into the previous connector turn.
After staging pits, close the pitted XML with torcs-close-loop2 --profile post-pit, then accept it only after trackgen -z reports the expected pit count, side-clearance dry-run reports no anomalies, and side-overlap dry-run reports no unresolved overlaps.
Run elevation after the layout and any curbs or pits for this pass are accepted. These tools add road heights and slopes without changing the horizontal layout. A road profile controls the driving surface; a terrain heightmap or relief file guides the surrounding landscape. An offset corridor samples parallel lines beside the source centerline to capture nearby ground heights.
Choose the route matching your data:
The bundled road template keeps Left Side and Right Side banking level (flat across their widths). Use tangent only when you want the road's sideways tilt to continue across the grass or shoulder.
Prepare local downloaded LiDAR/DEM tiles before importing them. This command writes a normal GeoTIFF mosaic and can convert vertical grid values to meters while preserving the source horizontal CRS. Use --dry-run --report ... with no --output for inspection only. Use --vertical-unit m when the grid values are already meters, ft for international feet, or us-ft for US survey feet:
For high-resolution DTM/LiDAR rasters, prefer importing the full road surface in source coordinates. --road-width is the real sampled road width, not Main Track/width from TORCS. Check horizontal and vertical units separately before import: a GeoTIFF CRS can correctly describe XY in feet while the grid values are also feet, meters, or another vertical datum/unit. Reprojecting or CRS metadata only solves the horizontal transform; convert the raster values to meters before profile import and terrain heightmap generation, then use the meter-valued raster consistently for profile, apply, and diagnostics. Projected non-meter horizontal CRS units are handled by scale fitting automatically; geographic rasters must be reprojected to a projected CRS before road-surface cross-section import:
Apply the raster road-surface profile directly when both center elevation and banking are acceptable:
Use the default banking width policy when TORCS Main Track/width matches the sampled real road width. If the TORCS road width differs and the real edge height delta should be preserved, add --bank-width-policy edge-delta; add --bank-real-width <meters> only when the profile does not already contain sampling.real_road_width_m.
Manual or written banking data can be imported separately and applied without rewriting elevation. Banking zones describe the tilt over ranges of source stations:
When applying elevation and banking together, compose compatible source-frame or XML-frame profiles first. The composer preserves the shared station mapping and fails instead of mixing incompatible frames:
To change only banking while keeping existing road heights:
After applying a road-surface profile, render the profile-only road surface diagnostic before generating final assets:
Use the profile that was actually applied: road-surface-raster-profile.json for the direct raster workflow, road-surface-profile.json for a composed profile, or banking-profile-manual.json for banking-only validation. If the track was applied with --bank-width-policy edge-delta, pass the same --bank-width-policy edge-delta to the diff command, plus --bank-real-width <meters> when the profile lacks sampling.real_road_width_m.
For an OSM-sourced track, fetch a DEM corridor cache from the source centerline. It contains road-center samples and offset terrain lines:
Then apply the fetched cache to the accepted pitted XML. The tool keeps the first station at z = 0, smooths the road profile, writes segment z start, z end, profil start tangent, and profil end tangent, then updates trackgen terrain-generation inputs. It does not split segments or change plan-view closure.
Each --point station:elevation pair uses meters. For example, --point 800:18 supplies a height of 18 meters at a distance of 800 meters along the XML centerline. With the example's starting height of zero, that is 18 meters above the start. These are profile control points; smoothing can adjust the final road heights. Choose stations appropriate to your lap length and keep the end-to-start transition smooth.
Minimal manual profile example:
For reusable manual data, pass one or more CSV/JSON profile files with --profile. CSV columns can be station_m,elevation_m,weight. JSON can be {"samples": [{"station_m": 0, "elevation_m": 0}, ...]}. Multiple profiles are normalized relative to their own start elevation and fused by weighted average. Manual profile files only shape the road; --elevation-cache also supplies offset terrain heights for relief lines.
Terrain generation modes:
Trackgen terrain heightmaps are 8-bit grayscale PNGs stretched linearly between minimum altitude and maximum altitude. Trackgen maps the image over the generated track bounds plus border margin. The final road model is vertically normalized by subtracting the road's minimum z, so raster heightmap altitudes must include the same track_z_offset_m used in the elevation report. GfImgReadPng flips file rows into memory, and trackgen maps memory row 0 to terrain ymin; therefore PNG files generated for trackgen must store their top file row as ymax. If a heightmap is written in normal array row order, the terrain is consumed upside down and can look like a high plateau with the road in a canyon.
Raster terrain example:
Validate the generated terrain heightmap against the source raster before judging the in-game terrain:
The terrain PNG diagnostic reports trackgen_terrain_z - normalized_source_raster_z. White is near perfect, red means the terrain PNG is too high, blue means it is too low, and black means the source raster has no sample for that pixel. The renderer emulates trackgen's PNG row flip and road z normalization, so it catches row-order and vertical-frame mistakes that are invisible if you inspect the raw PNG directly. With 8-bit heightmaps, small residuals from quantization are normal.
Keep the local source tiles, the raw/normal mosaiced GeoTIFF, the meter-valued mosaic from torcs-prepare-raster if vertical conversion was required, and the imported raster profile under source/ so repeated applies do not need to refetch or remosaic tiles. Regenerating <track>-terrain-elevation.png through torcs-apply-elevation --terrain-elevation-map auto is still the safest path because the PNG depends on the accepted XML bounds, border margin, road track_z_offset_m, min/max altitude, row order, and any pit flattening. A standalone PNG writer would need to read the same raster-backed profile or apply report, require metric vertical values, and update the XML heightmap attributes atomically; do not hand-tile DEM data into a PNG without those values.
Tune terrain shape with --terrain-elevation-width, --relief-distances, --relief-height-falloff, --smooth-window, and --pit-flatten-strength. If --dry-run is set, the tool writes only the report and leaves XML, relief, and heightmap files untouched.
To diagnose a bad crest, phase error (hills shifted along the lap), or plan-view mismatch, render an elevation-difference SVG after applying elevation:
The SVG draws the real/source road outline in dashed green, the generated TORCS outline in dark strokes, and every sampled TORCS centerline point as a blue-white-red difference marker. The reported difference is track_z - DEM_z: white is near perfect, red means the track is too high, and blue means the track is too low. Use --elevation-cache for the DEM data being compared, and optionally --map-elevation-cache for a wider or coarser DEM corridor rendered as the background height map. The background defaults to the center DEM line only; use --map-lines all only when offset terrain corridors are known clean. The tool searches circular source-loop shifts during the plan-view fit, so a source start point that differs from the XML start does not by itself hide misalignment. When --profile-output is set, it also writes a direct station/elevation JSON profile sampled from the fitted DEM at the accepted XML centerline; apply that profile with torcs-apply-elevation --profile ... when landmark station warping puts hills in the wrong place.
To keep relief terrain with a plan-fit profile, pass a terrain-only copy of the same DEM cache as --elevation-cache, set its top-level weight to 0.0, and name it as a derivative such as <cache-stem>-terrainonly.json or <cache-stem>-terrain-only.json. torcs-apply-elevation resolves the profile's elevation_source and cache paths canonically, rejects ambiguous or unrelated matches, and applies the fit's station shift, direction, scale, and centerline zero to matching relief lines. The apply report records this under terrain_source_alignment, including source_shift_m, source_reversed, scale, and zero_offset_m.
If generated relief terrain looks like a canyon or plateau but the road-vs-DEM diff is reasonable, compare the relief .ac vertical range to generated road and terrain object ranges before changing the road profile. A large average terrain-road offset usually indicates the relief cache is in the wrong station or zero frame.
For England tracks with an OSM or other georeferenced source polyline, torcs-auto-elevation can do the raster discovery and import automatically. The EA LiDAR provider searches the Environment Agency/Defra tile API, prefers bare-earth DTM products, downloads matching tiles into a cache, mosaics multiple rasters when necessary, and imports a road profile from the accepted XML centerline. The search area is the source/track bbox plus --landscape-margin-m, which defaults to 1000; this intentionally caches a wider landscape raster even when the current road profile only samples the centerline:
The automatic command writes tile-search.json, downloaded tile zips under downloads/, extracted rasters under tiles/, elevation-ea-lidar-profile.json, elevation-ea-lidar-profile-report.json, elevation-ea-lidar-profile.svg, and auto-elevation-report.json in the output directory. With --apply, it calls torcs-apply-elevation using the imported profile and writes elevation-report.json; with --diff, it writes elevation-diff.svg and elevation-diff-report.json. For accepted raster-backed elevation, rerun torcs-apply-elevation with --profile <generated-profile> --terrain-elevation-map auto plus the compact relief flags from the raster terrain example, then render torcs-render-terrain-elevation-diff-png. If the source polyline uses local x/y coordinates instead of lat/lon, pass --polyline-crs; EA rasters default to --provider-crs EPSG:27700. After applying elevation, regenerate base geometry with trackgen -q; if raceline layers are in use, rerun trackgen -r; if Graphic/3d description points to a merged .acc, rerun accc -g so the final visual model is not stale.
For higher-resolution local DEM data, import a raster profile instead of calling an online elevation API. Prefer bare-earth DTM data such as Environment Agency LiDAR Composite DTM 1m/2m; OS Terrain 5 can be a fallback where LiDAR is unavailable. GeoTIFFs usually carry their own CRS for horizontal coordinates, but the raster grid's vertical unit must still be verified from product metadata, tile names, or known local elevation ranges. ESRI ASCII grids need --raster-crs, for example EPSG:27700 for British National Grid. The source polyline is used only to fit the accepted XML centerline into raster coordinates; the output profile stations come from the XML, so this avoids OSM way-name station warping:
If official/government LiDAR catalogs expose direct object-storage URLs that return HTTP 403 or AccessDenied, do not immediately fall back to coarse DEM data. Download the required tiles from the official portal, keep them under runtime/tracks/<category>/<name>/source/<elevation-dir>/tiles/, prepare a meter-valued mosaic with torcs-prepare-raster, and import that mosaic with torcs-import-elevation-raster. In the track readme.txt, describe generated elevations, terrain heightmaps, relief, and geometry as derived from the official dataset and note the manual-download/access issue; do not imply raw tiles are shipped unless they are packaged.
Then apply the imported profile with a smaller smoothing window than coarse DEM data so short crests are not flattened or shifted. For accepted raster-backed elevation, include the raster terrain flags from the terrain example above in this same pass unless the task is explicitly road-only:
Use torcs-svg-to-centerline when the source layout is vector geometry. It supports SVG paths, polylines, and polygons; all standard path commands; nested transforms; unitless, pixel, and absolute-unit SVG viewports; root percentage dimensions; local <use> references; published-length scaling; direction reversal; and explicit station-zero placement. Nested percentage viewports are rejected because they require parent-viewport layout that this authoring importer does not infer.
For a simple SVG containing one closed loop:
For an Illustrator or map SVG with several eras, labels, or decorative paths, restrict extraction to a semantic group or element:
The default selects the longest visible closed candidate. --element-id selects one element or group, and --candidate-index selects an index from the report. Use --reverse to change racing direction, --keep-start to preserve the SVG subpath start, or --start x,y to project station zero onto the loop near transformed SVG coordinates. Without either start option, the tool rotates station zero to a smooth low-curvature location. --curve-tolerance is measured after SVG transforms.
The output uses local metric coordinates and is not georeferenced merely because the SVG resembles a real circuit. For DEM/LiDAR work, preserve a separately aligned geographic source and record its CRS and transform evidence. Centerline-to-reference georeferencing remains a separate workflow.
Rebuild SVG-derived sources with the drawing profile, or drawing-detail when small kinks and chicanes must survive:
Use torcs-georeference-centerline after SVG or raster extraction when the local loop needs trustworthy geographic coordinates for alignment evidence, DEM/LiDAR discovery, or raster sampling. The command never treats agreement with generated TORCS XML as georeferencing.
When a geographic OSM/GPS loop represents substantially the same layout, use automatic closed-loop alignment. Rigid fitting preserves a source already scaled to a published length; use --transform similarity only when source scale is genuinely unknown:
For historical layouts that differ from the modern reference, use named, distributed control points instead of forcing a whole-loop match:
The control file contains reference_crs, optional reference metadata, and at least three entries with source: {x, y} and reference: {lat, lon} or projected {x, y} coordinates. Approximate coordinates are snapped to the local and reference loops. Controls must cover different sides of the circuit; two, clustered, or nearly collinear controls require --allow-weak-control-set and produce review status. Large snaps are rejected by --max-source-snap and --max-reference-snap-m instead of being hidden by a low post-snap residual.
The default EPSG:4326 latitude/longitude output can be consumed directly by elevation commands. A non-default --output-crs is recorded in the cache, but current elevation commands still require the same CRS explicitly through --polyline-crs. The metric fitting CRS is selected automatically; an explicit --fit-crs must use meter units. The report records fit/output/reference CRS, transform parameters, reference station shift or reversed correspondence, control snap distances, residuals, distribution metrics, and status. Rejected runs write the report and overlay but remove the requested centerline output. Always inspect georeference-overlay.svg; low residuals do not prove that selected landmarks or the reference source are historically correct.
Use torcs-image-to-centerline when no usable OSM centerline exists and the source is a raster track map, screenshot, scan, or hand drawing. The tool extracts the main colored loop, scales it to a user-provided real length, and writes the same local x/y centerline JSON consumed by torcs-rebuild-mainline.
Use the clean-map profile for high-contrast maps with a clear single track line:
Use the antialias-ridge profile for antialiased scans or maps where the clean-map profile produces visible stair-step wiggles. It is slower, but usually gives a better centerline because it links Steger-style subpixel ridge candidates instead of integer pixel centers:
Use the skimage-map profile for thick or jagged raster track strokes that benefit from scikit-image morphology and skeletonization:
Then rebuild from the generated source with the drawing profile. This keeps more detail than the default OSM-oriented profile while filtering short raster wiggles from straights and wide bends:
If a small chicane or kink is washed out by the drawing profile, use the detail-preserving drawing profile instead of weakening source smoothing globally:
Stable extraction options:
Always inspect the preview SVG before rebuilding. If the map has branches, pit lanes, alternate layouts, red labels, or heavy decoration, crop or adjust thresholds until the preview shows one clean closed loop.
Low-level ridge diagnostics remain available only where needed for troubleshooting. Failed stabilization, control-spline, and upscale experiments are not part of the supported raster workflow.
For large image-derived tracks, use trackgen -q during repeated visual iterations and reserve trackgen -a -q for final full generation. Plain trackgen -q regenerates the track AC much faster and still reports closure metrics.
Use torcs-render-track-map after the final XML plan-view geometry is accepted. The command reconstructs the loop from the current XML segment order. Its combined white start/direction marker crosses the start line and bends into a filled arrow on the left side of travel, pointing in the racing direction. The PNG uses the standard TORCS 1024x1024 transparent RGBA presentation and omits labels and pit highlighting so it remains clear in the track-selection menu. Both modern Main Track/Track Segments and legacy Main Track/segments XML are supported without modifying the input files.
Run these examples from torcs/torcs/:
The renderer fits the actual track stroke and combined marker outline into the image, preserving aspect ratio. The default visible margin is max(2, size/128) pixels (8px at 1024, 2px at 256), with an additional 3px rendering allowance for antialiasing. Marker space is reserved only where it protrudes, rather than on every side. Elongated tracks naturally leave more space along their shorter dimension.
Both commands accept:
The optional single-track review SVG uses a neutral dark background and contains a track polyline and a start-marker group using the same fitted geometry as the PNG.
The batch command visits <tracks-root>/<category>/<name>/ directories in sorted order, reads <name>.xml, and writes <name>.png alongside it. Missing PNGs are created and existing PNGs are overwritten; nested source directories and auxiliary XML files are not scanned. Each PNG is rendered in a temporary directory beside its destination and verified before replacing it, so a failed render leaves the previous map intact. Replacements preserve existing file permissions; new maps use normal file-creation permissions (respecting the process umask on POSIX). Batch rendering generates PNGs only; use the single-track command for review SVGs.
Output reports CREATED, UPDATED, or FAILED for each track, followed by created/overwritten/failed counts. Missing XML, invalid geometry, and write failures are reported per track while the batch continues. Exit status is 0 only when all tracks succeed, otherwise nonzero. A missing or empty tracks root is an error. Fix the reported tracks and rerun the command to refresh the collection.
After updating an existing editable installation, refresh the console entry points if the new command is unavailable:
Alternatively, invoke python -m torcs_track_tools.cli.render_track_maps with the same arguments through the Conda environment.
Use this after the no-pit mainline is accepted and validated in game. Keep pit lanes out of the mainline source cache; fetch pit lanes separately into source/pitlane-osm.json.
Fetch a known pitlane way or ordered way list:
Run report-only feasibility first and inspect the SVG overlay:
Proceed only when real_placement.status is real-placement-ok. The report records side consistency, projected span, and the selected low-curvature pit-main window. Rejections such as real-placement-rejected-curvature are expected for pit lanes that TORCS cannot represent cleanly as a side pit over a sufficiently straight host window. Feasibility is not a mutation guarantee: if the chosen window resolves to an almost host that cannot preserve the original endpoint pose, integration stops and you should use a manual exact host instead of loosening the pose gate.
Integrate from the accepted real placement:
The integrator preserves a no-pit backup at runtime/tracks/road/<name>/source/<name>-no-pits.xml before the first edit, uses the reported pit side by default, and restores side/border materials after pit entry, pit main, and pit exit so TORCS state carry-over does not leak pit materials around the rest of the track. If the edited XML is already a staging file under source/, the backup stays in that same source/ directory; it should not create source/source/.
Close and validate the pitted XML. Prefer the geometric closer with --profile post-pit on a staging XML, then promote the staging XML only after the report has "ok": true, the XML still starts at pit_main_b, trackgen -z reports the expected pit count, and side-clearance and side-overlap dry-runs are clean.
The post-pit profile preserves the Pit10 start-line convention by keeping pit_main_b as the first track segment when it is present and by appending a tiny terminal X lever at the absolute end of the segment list. It defaults to a documented target-dy = 0.002 tolerance for known trackgen millimeter-scale quantization after pit-start rotation; pass an explicit stricter --target-dy only when the track closes cleanly under that limit. If direct --profile post-pit closure stalls on an otherwise sound pitted XML, use a two-step staging fallback: close the same pitted staging XML once without --profile post-pit, then run the geometric closer again with --profile post-pit on that pre-closed staging XML. Promote only the second result, after it reports "ok": true, starts at pit_main_b, and final trackgen -z reports the expected pit count. If the geometric closer is unavailable or still fails, use the legacy torcs-close-loop --profile post-pit workflow as a fallback diagnostic.
Final checks:
Run these final trackgen commands with workdir=torcs/torcs/runtime so tracks/... resolves against the runtime tree.
Accept the pitted track only after pits = 20 unless intentionally overridden, the closure report has "ok": true, the side-clearance dry-run reports no anomalies, the side-overlap dry-run reports no unresolved overlaps, and the in-game pit entry/exit plus side/border material transitions look correct.
Automatic synthetic host selection uses these modes:
torcs-integrate-pitlane safety gates check absolute final trackgen -z closure when safety is enabled. For the staged workflow above, --stage-for-closure intentionally skips immediate safety gates, then the geometric post-pit closer performs closure on the staging XML. Accept only if final trackgen -z passes.
If an almost manual host reports a large pose error, the proposed pit straight does not rejoin the original chain at the same endpoint even if total length and heading look plausible. Prefer a different exact host or an intentional local layout refit around the pit area; do not relax the pose gate unless the final staged XML is closed, overlap-safe, and validated in game.
Use manual host selection when OSM pitlane data is unavailable, ambiguous, rejected, produces a non-pose-preserving almost host, or the track is fictional/legacy. In manual mode the user-provided --pit-side is authoritative and OSM real-placement selection is skipped.
Single host segment:
Contiguous host chain:
Rules for manual mode:
Wrapping example: if the intended host starts near the end of the current XML order and continues after the XML start, keep the real driving order in the arguments:
The report records this as manual_wrap_rotation. Final pitted XML is still rotated to pit_main_b after insertion.
Prefer relation-based layout discovery first. List candidates before fetching when a relation may include multiple layouts, pit lanes, shortcuts, or service roads:
Then fetch the selected racing layout. Use a label shown by --list-layouts, for example main, all, unlabeled, forward, or another relation role:
Other useful lookup modes:
Use direct way IDs when the relation contains pit lanes, shortcuts, kart tracks, service roads, or multiple layouts that cannot be separated by role:
If automatic OSM discovery is ambiguous, useful user-provided data is:
Manual discovery options:
Relation-oriented query by location:
Before fitting, inspect the cache summary and reject noisy sources early:
| Scenario | Action |
|---|---|
| Track races in the wrong direction | Rebuild with --reverse-input; do not reverse XML by hand. |
| Relation includes pit lane | Refetch/filter OSM; keep pits out of no-pit mainline conversion. |
| Relation includes shortcuts or alternate layouts | Use --list-layouts or manually curated --way-ids. |
| Fit is slow after reversing | Ensure current tools are installed; avoid --no-fit-cache unless the cached DP draft is suspected stale. |
| Closure reports a sub-millimeter Y residual such as 0.000977 | This is within the default 0.001 m Y gate; mention the residual in the summary and keep other axes strict. |
| Direct pitted --profile post-pit close stalls | Pre-close the pitted staging XML once with geometric closure without --profile post-pit, then close that staging XML again with --profile post-pit; promote only the final pit-start-preserving XML. |
| Pit host names changed after curbs | Re-read the current XML and select from the post-curb segment names; outside curbs can split hosts into -pre, -curb, and -post pieces. |
| Outside curb splits disturb closure | Prefer whole-segment outside curb assignment with --segment-fit-ratio 0, then rerun trackgen -z, side-clearance dry-run, side-overlap dry-run, base generation, raceline generation, and ACC merge. |
| Pits, curbs, or elevation changed after raceline merge | Regenerate the base .ac, rerun trackgen -r, and rerun accc -g; do not keep an .acc merged against stale XML geometry. |
| Almost pit host has a large pose error | Use an exact host or locally refit the surrounding track geometry so the pit straight is part of the intended layout; do not accept large pose errors as a shortcut. |
| Baseline angle is above 2 degrees but closure seems plausible | torcs-rebuild-mainline defaults to a wider baseline gate; final promotion still requires strict closure targets. |
| A previous run was killed | Check and remove stale runtime/.trackgen-<category>-<name>.lock if present. |
| trackgen cannot find tracks/.../<name>.xml | Run trackgen from runtime/, or pass tools a correct --runtime-dir. |
| Closure config names segments that no longer exist | Rerun closure with --force-reselect rather than editing the JSON manually. |
| TORCS lists throwaway zz-* tracks | Remove local verification folders from runtime/tracks/... before manual testing. |
| Menu warns about missing <name>.png | Generate <name>.png with torcs-render-track-map. |
Accept generated live XML only after:
These checks are for changes to the Python tools themselves. Track authors can use the workflow reports and in-game validation above. Run the local quality suite from torcs/torcs/src/tools/tracktools/ when you want tests plus coverage and code metrics in one pass:
If PowerShell blocks local scripts, run the same suite with a per-process policy bypass:
The quality runner writes reports under build/quality/, including branch coverage HTML/JSON/XML, radon cyclomatic complexity, maintainability index, xenon thresholds, cognitive complexity, bandit, and vulture output. By default ruff, pytest, a 75% line coverage floor, and a 65% branch coverage floor are hard gates; advisory tools are summarized but do not fail the command unless --strict-advisory is passed. Raise --coverage-fail-under <percent> and --branch-fail-under <percent> only after adding characterization tests and confirming the new baseline is stable.