RaceMatrix Circuit JSON Format Specification

작성자: KiHyunKangKiHyunKang | 작성일: 2026-04-07 01:07
목록
문서타입: technical
보안수준: public
태그: json circuit format package overlay

RaceMatrix Circuit JSON Format Specification

Status: current as of 2026-06-15

Canonical implementation sources:

  • app/models/circuit.rb
  • app/models/circuit_json_canonicalizer.rb
  • app/assets/javascripts/circuit_json.js
  • app/views/circuits/_form.html.erb
  • app/controllers/circuits_controller.rb
  • app/models/operations_overlay.rb
  • app/models/circuit_package.rb
  • app/services/circuit_package_zip_exporter.rb

This document describes the JSON formats used by RaceMatrix for circuit layout export/import and for OBS-oriented operations data.

If this document and the code disagree, the code is authoritative.

Format Family

RaceMatrix now treats circuit data as three related JSON documents:

  1. Circuit Layout JSON
    • The base geometry and casual-user circuit metadata.
    • Used by the normal circuit editor and weekend-driver/data-logger workflows.
    • Does not embed race-control or event/session operating data.
  2. Race Operations Overlay JSON
    • A sidecar document attached to one exact base layout.
    • Used for OBS/race-control data such as timing points, operational zones, pit-lane semantics, and verification metadata.
  3. Circuit Package JSON
    • A manifest that declares compatible layout and overlay files.
    • The preferred OBS import/export provenance unit.

Base layout variants are not stored in a layouts[] array inside a layout file. Each layout is an independent Circuit Layout JSON file. A Circuit Package JSON file groups those independent files.

Circuit Layout JSON

Scope

Circuit Layout JSON is used for:

  • browser-based export from the circuit show page
  • browser-based export from the circuit editor
  • server-side layout download from GET /circuits/:id/download_json
  • browser-based import into the circuit editor
  • manual backup and exchange with third-party tools
  • layout files referenced by Circuit Package JSON

File extension:

  • .json

Encoding:

  • UTF-8

MIME type:

  • application/json

Current Export Version

RaceMatrix currently exports layout JSON with:

{
  "export_version": "2.3"
}

The 2.3 layout shape adds stable layout identity and canonical content hashing fields while keeping the previous geometry fields compatible with the browser editor.

Top-Level Object

Typical shape:

{
  "name": "Example Circuit",
  "description": "Example export",
  "center_lat": 37.1234567,
  "center_lng": 127.1234567,
  "geofence_radius": 640,
  "zoom_level": 15,
  "track_points": [],
  "pitlane_points": [],
  "sectors": [],
  "corners": [],
  "profile_id": "racematrix:circuit:...",
  "layout_id": "racematrix:layout:example-circuit:...",
  "layout_revision": 1,
  "layout_content_hash": "sha256:...",
  "length": 0,
  "pitlane_length": 0,
  "circuit_type": "closed",
  "road_width": 20.0,
  "verified": false,
  "creator": {
    "name": null,
    "email": null
  },
  "created_at": "2026-06-15T00:00:00+09:00",
  "updated_at": "2026-06-15T00:00:00+09:00",
  "exported_at": "2026-06-15T00:00:00+09:00",
  "export_version": "2.3"
}

Top-Level Fields

Field Type Exported by RaceMatrix Imported by editor Notes
name string yes yes Circuit name
description string yes yes Free-form description
center_lat number yes yes WGS84 latitude
center_lng number yes yes WGS84 longitude
geofence_radius number yes no Computed on export if absent in source data
zoom_level integer yes yes Defaults to 15
track_points TrackPoint[] yes yes Main circuit polyline
pitlane_points TrackPoint[] yes yes Optional pitlane polyline
sectors Sector[] yes yes Optional sector metadata
corners Corner[] yes yes Optional corner metadata
profile_id string yes yes Stable circuit/profile identity; generated when absent
layout_id string yes yes Stable layout identity; generated when absent
layout_revision integer yes yes Starts at 1; increments when the canonical layout hash changes
layout_content_hash string yes yes sha256: hash of canonical layout hash payload
length number yes no Track length in meters
pitlane_length number yes no Pitlane length in meters
circuit_type "closed" or "open" yes yes Defaults to "closed"
road_width number or null yes yes Default road width in meters
verified boolean yes no Show-page export preserves it; form export writes false
creator Creator yes no Informational metadata only
created_at string sometimes no Exported when known
updated_at string sometimes no Exported when known
exported_at string yes no ISO 8601 timestamp created at export time
export_version string yes no Currently "2.3"

Notes:

  • profile_id, layout_id, and layout_content_hash are required by the model but generated automatically for existing records or imported layouts that do not have them.
  • The browser importer preserves profile_id, layout_id, layout_revision, and layout_content_hash when present. On save, the server recomputes layout_content_hash from geometry and layout-affecting metadata.
  • Unknown extra fields are ignored by the browser importer. Operational data must use Race Operations Overlay JSON rather than relying on ignored unknown layout fields.

Layout Content Hash

layout_content_hash is computed by CircuitJsonCanonicalizer.content_hash over Circuit#layout_hash_payload.

Current hash payload:

{
  "export_version": "2.3",
  "circuit_type": "closed",
  "road_width": 20.0,
  "track_points": [],
  "pitlane_points": [],
  "sectors": [],
  "corners": []
}

Included in the hash:

  • export_version
  • circuit_type
  • road_width
  • formatted track_points
  • formatted pitlane_points
  • sectors
  • corners

Excluded from the hash:

  • name
  • description
  • center_lat
  • center_lng
  • geofence_radius
  • zoom_level
  • length
  • pitlane_length
  • verified
  • creator
  • created_at
  • updated_at
  • exported_at
  • profile_id
  • layout_id
  • layout_revision
  • layout_content_hash
  • Race Operations Overlay JSON
  • Circuit Package JSON
  • event/session operating data

Canonical JSON rules:

  • object keys are stringified and sorted lexicographically
  • arrays keep their original order
  • BigDecimal and Float values are rounded to 7 decimal places
  • integer-valued rounded numbers are emitted as integers
  • non-finite numbers are invalid
  • the hash is sha256: plus the SHA-256 digest of the canonical JSON string

Nested Types

TrackPoint

{
  "lat": 37.1234567,
  "lng": 127.1234567,
  "ele": 42.15,
  "width": 11.5
}
Field Type Required Notes
lat number yes Latitude in decimal degrees
lng number yes Longitude in decimal degrees
ele number no Elevation in meters
width number no Per-point road width override in meters

Both track_points and pitlane_points use this point shape. The current server formatter preserves point width for main track points and elevation for track and pit-lane points.

Sector

{
  "name": "Sector 1",
  "start": 0,
  "end": 125
}
Field Type Required Notes
name string no Exporters usually include it
start integer yes Zero-based index into track_points
end integer yes Zero-based index into track_points

RaceMatrix validation rules:

  • sectors are limited to 255 entries
  • start and end must be valid track_points indices
  • both indices must fit in uint16 so the circuit remains BCF-exportable
  • for closed circuits, the final sector may use end: 0 to mean wrap-around to the start point

Corner

{
  "name": "Turn 1",
  "number": 1,
  "point": 87
}
Field Type Required Notes
name string no Exporters usually include it
number integer no Display/order metadata used by the editor
point integer yes Zero-based index into track_points

RaceMatrix validation rules:

  • corners are limited to 255 entries
  • point must be a valid track_points index
  • point must fit in uint16 so the circuit remains BCF-exportable

Creator

{
  "name": "Jane Doe",
  "email": "jane@example.com"
}
Field Type Required Notes
name string or null no Informational only
email string or null no Informational only

Race Operations Overlay JSON

Race Operations Overlay JSON is a first-class sidecar format for operational data. It is not stored as an unknown field in Circuit Layout JSON.

Typical shape:

{
  "overlay_type": "racematrix.race_operations_overlay",
  "schema_version": "1.0",
  "overlay_id": "racematrix:ops-overlay:...",
  "overlay_revision": 1,
  "name": "Race Control",
  "usage": "race_control",
  "base_circuit": {
    "profile_id": "racematrix:circuit:...",
    "layout_id": "racematrix:layout:example-circuit:...",
    "layout_revision": 1,
    "layout_content_hash": "sha256:..."
  },
  "timing_points": [
    {
      "id": "timing_...",
      "role": "start_finish",
      "domain": "main_track",
      "shape": {
        "kind": "line",
        "left": { "lat": 37.1234, "lng": 127.1234 },
        "right": { "lat": 37.1235, "lng": 127.1235 },
        "lateral_min_m": -20,
        "lateral_max_m": 20
      },
      "valid_direction": "bidirectional"
    }
  ],
  "operational_zones": [
    {
      "id": "zone_...",
      "kind": "flag_zone",
      "domain": "main_track",
      "shape": {
        "kind": "polygon",
        "points": []
      }
    }
  ],
  "pit_lane": {},
  "verification": {}
}

Required identity/reference fields:

  • overlay_type
  • schema_version
  • overlay_id
  • overlay_revision
  • name
  • base_circuit.layout_id
  • base_circuit.layout_content_hash

overlay_content_hash is intentionally not embedded in the overlay file. RaceMatrix stores it on the OperationsOverlay record and publishes it from Circuit Package JSON and OBS ledger/provenance fields. If an imported overlay JSON contains overlay_content_hash, RaceMatrix ignores it when exporting and hashing the overlay payload.

Validation and lifecycle:

  • overlay_type must be racematrix.race_operations_overlay when present.
  • timing_points and operational_zones must be arrays when present.
  • pit_lane must be an object when present.
  • The overlay stores base_circuit.profile_id, base_circuit.layout_id, base_circuit.layout_revision, and base_circuit.layout_content_hash.
  • Timing lines use shape.kind: "line" with explicit left and right endpoints, lateral range, domain, and valid_direction; they are not inferred from centerline distance and width.
  • Operational zone shapes use shape.kind, not shape.type. Current editor-created kinds are marker, polygon, and track_range.
  • If base_circuit.layout_content_hash matches the current layout hash, the overlay can be valid.
  • If the base layout hash differs, RaceMatrix marks the overlay review_required unless it has been explicitly marked incompatible, remapped, or deprecated.
  • Editing overlay content increments overlay_revision and recomputes overlay_content_hash.

Overlay status values:

  • draft
  • valid
  • review_required
  • incompatible
  • remapped
  • deprecated

Circuit Package JSON

Circuit Package JSON is the package-root manifest used to hand layouts and overlays to OBS.

The manifest uses layouts[] for forward compatibility, but RaceMatrix v1 is a single-layout package MVP. RaceMatrix v1 package validation requires exactly one layout entry and the ZIP exporter writes only that one current circuit layout. Multi-layout venue packages remain a future extension that will need file-backed importer/exporter support.

Typical shape:

{
  "package_type": "racematrix.circuit_package",
  "schema_version": "1.0",
  "package_id": "racematrix:circuit-package:...",
  "package_revision": 1,
  "package_content_hash": "sha256:...",
  "name": "Example Circuit",
  "layouts": [
    {
      "layout_id": "racematrix:layout:example-circuit:...",
      "name": "Example Circuit",
      "file": "layouts/example-circuit.json",
      "layout_revision": 1,
      "layout_content_hash": "sha256:..."
    }
  ],
  "overlays": [
    {
      "overlay_id": "racematrix:ops-overlay:...",
      "name": "Race Control",
      "file": "overlays/race-control.json",
      "overlay_revision": 1,
      "overlay_content_hash": "sha256:...",
      "base_layout_id": "racematrix:layout:example-circuit:...",
      "base_layout_content_hash": "sha256:...",
      "usage": "race_control"
    }
  ],
  "default_layout_id": "racematrix:layout:example-circuit:...",
  "default_overlays_by_usage": {
    "race_control": "racematrix:ops-overlay:..."
  }
}

Required package fields:

  • package_type
  • schema_version
  • package_id
  • package_revision
  • package_content_hash
  • layouts
  • overlays
  • default_layout_id
  • default_overlays_by_usage

RaceMatrix v1 package rules:

  • layouts must contain exactly one entry.
  • The single layout must match the current circuit layout_id and layout_content_hash.
  • Overlay entries may be empty, but every listed overlay must reference the package layout.
  • Package validation is DB-backed manifest validation. It checks manifest shape, path safety, package hash, current layout hash, local overlay hashes, and overlay base layout references.
  • RaceMatrix v1 does not import a loose package directory and read arbitrary layout/overlay files from disk. Actual loose-file content verification is part of OBS import, ZIP package import, or a future RaceMatrix file-backed importer.

Package path rules:

  • layouts[].file and overlays[].file are relative to the package root.
  • Empty paths are invalid.
  • Absolute Unix paths are invalid.
  • Absolute Windows-style paths are invalid.
  • URL or URI-like paths are invalid.
  • Any path containing .. as a path segment is invalid.

Package hash rules:

  • layout_content_hash is the canonical content hash of the referenced Circuit Layout JSON.
  • overlay_content_hash is the canonical content hash of the referenced Race Operations Overlay JSON, excluding any overlay_content_hash field if a third-party file includes one.
  • package_content_hash is computed from canonical package JSON with package_content_hash itself excluded.
  • ZIP/raw file hashes are not represented by these fields.

Default overlays:

  • Use default_overlays_by_usage, not a single default_overlay_id.
  • Typical usage keys include race_control, trackday, drift, and endurance.
  • Each value must reference an overlay listed in overlays.

Package status values:

  • draft
  • valid
  • review_required
  • incompatible
  • invalid
  • deprecated

Import modes:

  • package_loose_files
  • package_zip
  • package_api
  • direct_with_synthetic_manifest

Hash mismatch status values:

  • invalid
  • review_required
  • incompatible
  • remapped

OBS Import Policy

OBS should use Circuit Package JSON as the default import and provenance unit.

On package import, OBS should verify:

  • package manifest canonical hash
  • each layout file canonical hash
  • each overlay file canonical hash
  • each overlay base_circuit.layout_id
  • each overlay base_circuit.layout_content_hash
  • package manifest base_layout_id and base_layout_content_hash references

RaceMatrix v1 package validation is narrower: it validates a DB-backed single-layout manifest and generated ZIP export. OBS remains responsible for reading loose package files and verifying their actual canonical content hashes during the first integration path.

Mismatch handling:

  • If package_content_hash, a layout file hash, or an overlay file hash differs from the manifest, the import is invalid and should be rejected.
  • If an overlay base_circuit.layout_content_hash differs from the actual layout hash, automatic remap is forbidden.
  • Base layout hash mismatch should be stored as review_required or incompatible.
  • If a user explicitly remaps/approves, create a new overlay revision and hash and mark that result remapped.

Direct layout+overlay import is allowed for simple workflows, but OBS should create an internal synthetic import manifest so ledger/provenance records remain equivalent to package import.

OBS ledger/provenance should store:

  • package_id
  • package_revision
  • package_content_hash
  • layout_id
  • layout_revision
  • layout_content_hash
  • overlay_id
  • overlay_revision
  • overlay_content_hash
  • import_mode
  • hash mismatch status: invalid, review_required, incompatible, or remapped

Initial integration order:

  1. loose package files
  2. ZIP package export
  3. API package pull

Compatibility Notes

  • The normal circuit editor remains focused on the base layout used by amateur drivers and data-loggers.
  • Operations overlay editing is intentionally separate from the normal circuit editor UI.
  • Circuit Layout JSON remains independent of BCF compatibility, but RaceMatrix model validation still limits sectors and corners to 255 so the same circuit can be exported to BCF.
  • Third-party tools may include extra top-level metadata, but RaceMatrix’s browser importer ignores unknown fields.
  • Operational fields that must round-trip should be stored in Race Operations Overlay JSON, not as unknown fields in Circuit Layout JSON.

Minimal Layout Example

{
  "name": "Example Circuit",
  "center_lat": 37.1234567,
  "center_lng": 127.1234567,
  "track_points": [
    { "lat": 37.1234, "lng": 127.1234 },
    { "lat": 37.1235, "lng": 127.1235 }
  ],
  "pitlane_points": [],
  "sectors": [],
  "corners": [],
  "profile_id": "racematrix:circuit:...",
  "layout_id": "racematrix:layout:example-circuit:...",
  "layout_revision": 1,
  "layout_content_hash": "sha256:...",
  "circuit_type": "closed",
  "export_version": "2.3"
}