Maliput GeoPackage Schema
This document describes the GeoPackage schema used by maliput_geopackage to store HD-map data compatible with the maliput road network abstraction.
Overview
The maliput GeoPackage format stores road network data in a SQLite database following the OGC GeoPackage standard. This format provides:
Portability: Single-file database that works across platforms
Spatial indexing: Efficient geometric queries via SpatiaLite extensions
Self-documenting: Schema is queryable and human-readable
Tooling support: Compatible with QGIS, GDAL, and other GIS tools
ToC
Schema Definition
GeoPackage compliance notes
The SQL schema in this document is designed for use with GeoPackage files:
Mandatory GeoPackage tables: a GeoPackage used for vector features MUST include the
gpkg_spatial_ref_sys,gpkg_contents, andgpkg_geometry_columnstables per the GeoPackage specification. These tables provide SRS definitions, a registry of content layers, and geometry metadata.Geometry storage: GeoPackage feature geometries are stored as GeoPackageBinary BLOBs (not as WKT text).
Feature table primary keys: GeoPackage feature tables require an
INTEGER PRIMARY KEYcolumn (rowid alias) for each user feature table. Text-based UUID primary keys prevent correct linking withgpkg_metadata_referenceand some GeoPackage tools.
Minimal example SQL:
-- create required GeoPackage core tables (from the spec)
CREATE TABLE gpkg_spatial_ref_sys (
srs_name TEXT NOT NULL,
srs_id INTEGER PRIMARY KEY,
organization TEXT NOT NULL,
organization_coordsys_id INTEGER NOT NULL,
definition TEXT NOT NULL,
description TEXT
);
CREATE TABLE gpkg_contents (
table_name TEXT NOT NULL PRIMARY KEY,
data_type TEXT NOT NULL,
identifier TEXT UNIQUE,
description TEXT DEFAULT '',
last_change DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
min_x DOUBLE,
min_y DOUBLE,
max_x DOUBLE,
max_y DOUBLE,
srs_id INTEGER,
CONSTRAINT fk_gc_srs FOREIGN KEY (srs_id)
REFERENCES gpkg_spatial_ref_sys(srs_id)
);
CREATE TABLE gpkg_geometry_columns (
table_name TEXT NOT NULL,
column_name TEXT NOT NULL,
geometry_type_name TEXT NOT NULL,
srs_id INTEGER NOT NULL,
z TINYINT NOT NULL,
m TINYINT NOT NULL,
CONSTRAINT pk_geom_cols PRIMARY KEY (table_name, column_name),
CONSTRAINT fk_geom_table FOREIGN KEY (table_name)
REFERENCES gpkg_contents(table_name),
CONSTRAINT fk_geom_srs FOREIGN KEY (srs_id)
REFERENCES gpkg_spatial_ref_sys(srs_id)
);
CREATE TABLE gpkg_extensions (
table_name TEXT,
column_name TEXT,
extension_name TEXT NOT NULL,
definition TEXT NOT NULL,
scope TEXT NOT NULL,
CONSTRAINT ge_tce UNIQUE (table_name, column_name, extension_name)
);
Coordinate reference system choice
This GeoPackage schema adopts a custom local Cartesian coordinate reference system aligned with maliput’s inertial frame. While GeoPackage is commonly used with Earth-referenced coordinate systems (e.g., WGS84), maliput fundamentally operates in a flat, right-handed Cartesian space with metric units and no notion of geodesy, Earth curvature, or map projections. Using a geodetic or projected CRS would therefore introduce unnecessary coordinate transformations, precision loss, and additional complexity without providing functional benefits to maliput’s road network abstractions. The GeoPackage specification permits custom, non-Earth-referenced spatial reference systems, allowing the schema to directly encode geometry in the same coordinate space expected by maliput. By choosing a local Cartesian CRS, the schema preserves geometric fidelity, simplifies parsing and ingestion, and maintains a clear semantic alignment between the stored data and maliput’s API. It is up to the creator of the geopackage file to convert the points into this local coordinate system.
The custom SRS is defined as follows:
INSERT INTO gpkg_spatial_ref_sys (
srs_name,
srs_id,
organization,
organization_coordsys_id,
definition,
description
) VALUES (
'maliput_local_cartesian',
100000,
'MALIPUT',
1,
'LOCAL_CS["maliput",
LOCAL_DATUM["map_origin", 0],
UNIT["metre", 1],
AXIS["x", EAST],
AXIS["y", NORTH],
AXIS["z", UP]
]',
'Local Cartesian coordinate system aligned with maliput inertial frame'
);
Core Tables
maliput_metadata
Stores key-value pairs for road network configuration.
CREATE TABLE maliput_metadata (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
Common metadata keys:
Key |
Description |
Example |
|---|---|---|
|
Tolerance for linear operations (meters) |
|
|
Tolerance for angular operations (radians) |
|
|
Scale length for road geometry |
|
|
Translation vector |
|
junctions
Defines Junctions in the road network. A Junction is a container object that groups together related road Segments. It represents a specific, distinct volumetric region of the road network—such as an intersection, a parking lot, or a stretch of highway.
CREATE TABLE junctions (
junction_id TEXT PRIMARY KEY,
name TEXT
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the junction |
|
TEXT |
Human-readable name (optional) |
segments
Defines Segments within Junctions. A Segment is a container that groups together adjacent Lanes sharing the same continuous road surface geometry.
CREATE TABLE segments (
segment_id TEXT PRIMARY KEY,
junction_id TEXT NOT NULL,
name TEXT,
FOREIGN KEY (junction_id) REFERENCES junctions(junction_id)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the segment |
|
TEXT |
Parent junction ID |
|
TEXT |
Human-readable name (optional) |
lane_boundaries
Stores shared boundary geometries. For GeoPackage compliance, geometries should be stored as BLOB (GeoPackageBinary format). These are referenced from lanes by ID to avoid duplicating identical boundary geometry when adjacent lanes share a common edge.
CREATE TABLE lane_boundaries (
id INTEGER PRIMARY KEY,
boundary_id TEXT UNIQUE NOT NULL,
geom BLOB NOT NULL -- GeoPackageBinary BLOB for spatial compliance
);
Column |
Type |
Description |
|---|---|---|
|
INTEGER |
Auto-incremented primary key (required for GeoPackage compliance) |
|
TEXT |
Unique identifier for the boundary (human-friendly key) |
|
BLOB |
GeoPackageBinary BLOB encoding of LINESTRING geometry |
Given lane_boundaries are a spatial map feature, they need to be registered in the gpkg_contents and gpkg_geometry_columns tables:
INSERT INTO gpkg_contents (
table_name,
data_type,
identifier,
srs_id
) VALUES (
'lane_boundaries',
'features',
'Lane boundaries',
100000 -- Must be the same as the one chosen for gpkg_spatial_ref_sys
);
INSERT INTO gpkg_geometry_columns (
table_name,
column_name,
geometry_type_name,
srs_id,
z,
m
) VALUES (
'lane_boundaries',
'geom',
'LINESTRING',
100000, -- Must be the same as the one chosen for gpkg_spatial_ref_sys
1, -- We want z values
0
);
lanes
Defines lanes which reference left and right boundary geometries stored in the lane_boundaries table.
CREATE TABLE lanes (
lane_id TEXT UNIQUE NOT NULL,
segment_id TEXT NOT NULL,
lane_type TEXT DEFAULT 'driving',
direction TEXT DEFAULT 'forward',
left_boundary_id TEXT NOT NULL,
left_boundary_inverted BOOLEAN DEFAULT FALSE,
right_boundary_id TEXT NOT NULL,
right_boundary_inverted BOOLEAN DEFAULT FALSE,
FOREIGN KEY (segment_id) REFERENCES segments(segment_id),
FOREIGN KEY (left_boundary_id) REFERENCES lane_boundaries(boundary_id),
FOREIGN KEY (right_boundary_id) REFERENCES lane_boundaries(boundary_id)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the lane (human-friendly key) |
|
TEXT |
Parent segment ID |
|
TEXT |
Lane type: |
|
TEXT |
Travel direction: |
|
TEXT |
Reference to a |
|
BOOLEAN |
If TRUE, iterate left boundary points in reverse order |
|
TEXT |
Reference to a |
|
BOOLEAN |
If TRUE, iterate right boundary points in reverse order |
Connectivity Tables
branch_point_lanes
Defines how lanes connect at branch points. Branch points are the start and end points of lanes where they can connect to other lanes.
CREATE TABLE branch_point_lanes (
branch_point_id TEXT NOT NULL,
lane_id TEXT NOT NULL,
side TEXT NOT NULL CHECK (side IN ('a', 'b')),
lane_end TEXT NOT NULL CHECK (lane_end IN ('start', 'finish')),
FOREIGN KEY (lane_id) REFERENCES lanes(lane_id)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Identifier for the branch point |
|
TEXT |
Lane connected to this branch point |
|
TEXT |
Side of the branch point: |
|
TEXT |
Which end of the lane: |
Branch Point Semantics:
A branch point connects lane ends that meet at the same physical location
Side
alanes can transition to sideblanes (and vice versa)A straight road has two branch points: one at the start, one at the end
An intersection may have multiple lanes on each side
Example for a simple 2-lane road:
Lane 1: start ──────────────────> finish
Lane 2: start ──────────────────> finish
BranchPoint "bp_start":
- a-side: lane1/start, lane2/start
- b-side: (empty, or connected upstream lanes)
BranchPoint "bp_end":
- a-side: lane1/finish, lane2/finish
- b-side: (empty, or connected downstream lanes)
view_adjacent_lanes
Defines lateral adjacency between parallel lanes in the same segment.
Adjacency between lanes is derivable from shared lane_boundaries references. When two lanes share a common boundary such that one lane’s right_boundary_id equals the other’s left_boundary_id, they are adjacent (the second lane is on the right of the first). Likewise, when one lane’s left_boundary_id equals another’s right_boundary_id, the second lane is on the left of the first.
Because adjacency can be computed from lanes.left_boundary_id / lanes.right_boundary_id, we avoid duplicating this data in a table. Instead provide a read-only SQL VIEW that derives the adjacency on demand:
CREATE VIEW view_adjacent_lanes AS
SELECT
l1.lane_id AS lane_id,
l2.lane_id AS adjacent_lane_id,
CASE
WHEN l1.right_boundary_id = l2.left_boundary_id THEN 'right'
WHEN l1.left_boundary_id = l2.right_boundary_id THEN 'left'
ELSE NULL
END AS side
FROM lanes l1
JOIN lanes l2
ON l1.right_boundary_id = l2.left_boundary_id
OR l1.left_boundary_id = l2.right_boundary_id
WHERE l1.lane_id <> l2.lane_id
AND (
l1.right_boundary_id = l2.left_boundary_id
OR l1.left_boundary_id = l2.right_boundary_id
);
This keeps the schema normalized and guarantees adjacency consistency without requiring maintenance logic.
Road Markings Tables
Road markings describe the visual appearance of lane boundaries. They provide information about the type, color, weight, and geometry of markings at lane boundaries.
lane_markings
Stores lane marking information associated with lane_boundaries. Lane markings can vary along the length of a boundary (s-coordinate).
CREATE TABLE lane_markings (
marking_id TEXT UNIQUE NOT NULL,
boundary_id TEXT NOT NULL,
s_start REAL NOT NULL,
s_end REAL NOT NULL,
marking_type TEXT NOT NULL,
color TEXT DEFAULT 'white',
weight TEXT DEFAULT 'standard',
width REAL,
height REAL,
material TEXT,
lane_change_rule TEXT DEFAULT 'none',
FOREIGN KEY (boundary_id) REFERENCES lane_boundaries(boundary_id),
CHECK (s_start >= 0 AND s_end >= s_start)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the marking |
|
TEXT |
Reference to the boundary this marking is on |
|
REAL |
Start position along the boundary (s-coordinate) in meters |
|
REAL |
End position along the boundary in meters |
|
TEXT |
Type of marking: |
|
TEXT |
Color of the marking: |
|
TEXT |
Visual weight: |
|
REAL |
Width of the marking in meters (optional) |
|
REAL |
Height of the marking in meters (optional, for raised markings) |
|
TEXT |
Material description (e.g., |
|
TEXT |
Passing/lane change rule: |
Marking Types Reference:
solid: Continuous unbroken linedashed: Dashed/intermittent linedouble_solid: Two parallel solid linesbroken: Broken line (synonymous with dashed)double_broken: Two parallel broken linessolid_solid: Dual solid lines (same direction)solid_broken: One solid, one broken (direction dependent)broken_solid: One broken, one solid (direction dependent)
lane_marking_lines
Stores detailed line definitions for complex markings with multiple line components (e.g., double lines with different patterns).
CREATE TABLE lane_marking_lines (
line_id TEXT UNIQUE NOT NULL,
marking_id TEXT NOT NULL,
line_index INTEGER NOT NULL,
length REAL,
space REAL,
width REAL,
r_offset REAL,
color TEXT,
FOREIGN KEY (marking_id) REFERENCES lane_markings(marking_id)
);
Column |
Type |
Description |
|---|---|---|
|
INTEGER |
Auto-incremented identifier for this line component |
|
TEXT |
Reference to the parent marking |
|
INTEGER |
Order of this line within the marking (0-based) |
|
REAL |
Length of visible part of the line in meters |
|
REAL |
Gap/space between repeated segments in meters |
|
REAL |
Width of this specific line in meters |
|
REAL |
Lateral offset from the boundary in meters (positive = rightward) |
|
TEXT |
Color of this line component (may differ from parent marking) |
Notes:
For simple markings (single line), you can omit
lane_marking_linesentries and use only the top-levellane_markingsfields.For complex markings (double lines, multi-component patterns), populate
lane_marking_linesto describe each component.lengthandspacedefine repeating dash patterns: a marking repeats everylength + spacemeters.r_offsetallows representing markings that are offset perpendicular to the boundary direction.
Relationship to Lanes:
Lane markings are associated with lane_boundaries, not directly with lanes. Here’s how they connect:
Lane
├─ left_boundary_id ──→ Boundary
│ └─ Lane Markings (on this boundary)
│ └─ Lane Marking Lines (detailed components)
│
└─ right_boundary_id ──→ Boundary
└─ Lane Markings (on this boundary)
└─ Lane Marking Lines (detailed components)
Example Workflow:
To add a white dashed center line marking to a lane:
Identify the lane’s shared boundary (e.g.,
b_betweenshared betweenlane1andlane2)Create a
lane_markingentry referencing that boundary:INSERT INTO lane_markings (marking_id, boundary_id, s_start, s_end, marking_type, color, width, lane_change_rule) VALUES ('marking_lane1_lane2_center', 'b_between', 0.0, 100.0, 'dashed', 'white', 0.12, 'allowed');
Optionally, add detailed
lane_marking_linesif needed:INSERT INTO lane_marking_lines (marking_id, line_index, length, space, width) VALUES ('marking_lane1_lane2_center', 0, 3.0, 9.0, 0.12);
The marking now applies to the shared boundary between the two lanes, and both lanes implicitly “see” this marking on their common edge.
Traffic Control Tables
Traffic control tables define speed limits, traffic lights, and other regulatory elements that govern vehicle behavior on the road network.
speed_limits
Stores speed limit regulations associated with lanes. Each speed limit applies to a range along a lane’s s-coordinate (arc-length), similar to how lane_markings apply to ranges along boundaries.
Speed limits are per-lane (not per-boundary), matching how road regulations work: a speed limit governs traffic on a specific lane, potentially varying along its length.
CREATE TABLE speed_limits (
speed_limit_id TEXT UNIQUE NOT NULL,
lane_id TEXT NOT NULL,
s_start REAL NOT NULL,
s_end REAL NOT NULL,
max_speed REAL NOT NULL,
min_speed REAL DEFAULT 0.0,
description TEXT,
severity INTEGER DEFAULT 0,
FOREIGN KEY (lane_id) REFERENCES lanes(lane_id),
CHECK (s_start >= 0 AND s_end >= s_start),
CHECK (max_speed >= 0),
CHECK (min_speed >= 0 AND min_speed <= max_speed),
CHECK (severity >= 0)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the speed limit entry |
|
TEXT |
Reference to the lane this speed limit applies to |
|
REAL |
Start position along the lane (s-coordinate) in meters |
|
REAL |
End position along the lane in meters |
|
REAL |
Maximum allowed speed in meters per second (m/s) |
|
REAL |
Minimum required speed in m/s (default 0, i.e., stopping is allowed) |
|
INTEGER |
Enforcement level: |
|
TEXT |
Human-readable description (e.g., |
Unit Convention:
All speed values are stored in meters per second (m/s), consistent with maliput’s internal representation and the schema’s use of metric units throughout. Producers should convert from their source units (km/h, mph, etc.) when populating the table.
Source unit |
Conversion |
Example |
|---|---|---|
km/h |
value / 3.6 |
30 km/h → 8.33 |
mph |
value × 0.44704 |
25 mph → 11.18 |
Severity Semantics:
Value |
Meaning |
Example |
|---|---|---|
|
Strict |
Legally enforced limit (e.g., 50 km/h zone) |
|
Advisory |
Recommended limit (e.g., curve advisory speed) |
Design Notes:
Per-lane, not per-road: Following the OpenDRIVE model, speed limits are associated with individual lanes. This allows different limits per lane (e.g., a truck lane with lower limits). For a uniform limit across all lanes, insert one row per lane with the same speed value.
Multiple zones per lane: A lane may have multiple speed limit entries covering different s-ranges (e.g., 50 km/h for the first half, 30 km/h near a school zone).
Gaps are allowed: If no speed limit row covers a particular s-range, the lane has no explicit speed limit in that zone. Rule-loading code may choose to apply a default.
Mapping to maliput: Each row maps to one
maliput::api::rules::RangeValueRulewithSpeedLimitRuleTypeId(), zone defined by(lane_id, s_start, s_end), and range defined by(min_speed, max_speed, severity, description).
Example:
-- 50 km/h zone on lane_1 from start to station 80m
INSERT INTO speed_limits (speed_limit_id, lane_id, s_start, s_end, max_speed, description)
VALUES ('sl_lane1_zone1', 'lane_1', 0.0, 80.0, 13.89, '50 km/h zone');
-- 30 km/h school zone on lane_1 from 80m to end
INSERT INTO speed_limits (speed_limit_id, lane_id, s_start, s_end, max_speed, description)
VALUES ('sl_lane1_zone2', 'lane_1', 80.0, 100.0, 8.33, '30 km/h school zone');
-- Advisory curve speed on lane_2
INSERT INTO speed_limits (speed_limit_id, lane_id, s_start, s_end, max_speed, severity, description)
VALUES ('sl_lane2_curve', 'lane_2', 40.0, 60.0, 6.94, 1, '25 km/h curve advisory');
traffic_lights
Stores physical traffic light devices positioned in the road network. Each traffic light has an ID, position, and orientation.
CREATE TABLE traffic_lights (
traffic_light_id TEXT UNIQUE NOT NULL,
inertial_x REAL NOT NULL,
inertial_y REAL NOT NULL,
inertial_z REAL NOT NULL,
roll REAL DEFAULT 0.0,
pitch REAL DEFAULT 0.0,
yaw REAL DEFAULT 0.0,
name TEXT
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the traffic light |
|
REAL |
X-coordinate of traffic light position in inertial frame (meters) |
|
REAL |
Y-coordinate of traffic light position in inertial frame (meters) |
|
REAL |
Z-coordinate of traffic light position in inertial frame (meters) |
|
REAL |
Roll angle (rotation about x-axis) in radians |
|
REAL |
Pitch angle (rotation about y-axis) in radians |
|
REAL |
Yaw angle (rotation about z-axis) in radians |
|
TEXT |
Human-readable name for the traffic light (optional) |
Position/Orientation Notes:
Position is specified in the inertial frame (typically ENU - East-North-Up).
Rotation uses roll-pitch-yaw (Euler angles) in radians.
Typical orientation: yaw=π points the traffic light northward, yaw=0 points eastward.
bulb_groups
Bulb groups are collections of bulbs within a traffic light. A traffic light may have multiple groups (e.g., one for vehicles, one for pedestrians).
CREATE TABLE bulb_groups (
bulb_group_id TEXT UNIQUE NOT NULL,
traffic_light_id TEXT NOT NULL,
relative_x REAL DEFAULT 0.0,
relative_y REAL DEFAULT 0.0,
relative_z REAL DEFAULT 0.0,
roll REAL DEFAULT 0.0,
pitch REAL DEFAULT 0.0,
yaw REAL DEFAULT 0.0,
name TEXT,
FOREIGN KEY (traffic_light_id) REFERENCES traffic_lights(traffic_light_id)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the bulb group |
|
TEXT |
Parent traffic light ID |
|
REAL |
X-position relative to parent traffic light (meters) |
|
REAL |
Y-position relative to parent traffic light (meters) |
|
REAL |
Z-position relative to parent traffic light (meters) |
|
REAL |
Roll angle relative to parent (radians) |
|
REAL |
Pitch angle relative to parent (radians) |
|
REAL |
Yaw angle relative to parent (radians) |
|
TEXT |
Human-readable name (optional) |
Positioning:
Positions and rotations are relative to the parent traffic light.
This allows modeling traffic light assemblies with multiple signal heads positioned around a mounting structure.
bulbs
Bulbs are individual light elements within a bulb group. Each bulb has a color and type (round or arrow).
CREATE TABLE bulbs (
bulb_id TEXT UNIQUE NOT NULL,
bulb_group_id TEXT NOT NULL,
relative_x REAL DEFAULT 0.0,
relative_y REAL DEFAULT 0.0,
relative_z REAL DEFAULT 0.0,
color TEXT NOT NULL CHECK (color IN ('red', 'yellow', 'green')),
bulb_type TEXT NOT NULL CHECK (bulb_type IN ('round', 'arrow')),
FOREIGN KEY (bulb_group_id) REFERENCES bulb_groups(bulb_group_id)
);
Column |
Type |
Description |
|---|---|---|
|
TEXT |
Unique identifier for the bulb |
|
TEXT |
Parent bulb group ID |
|
REAL |
X-position relative to parent bulb group (meters) |
|
REAL |
Y-position relative to parent bulb group (meters) |
|
REAL |
Z-position relative to parent bulb group (meters) |
|
TEXT |
Color of the bulb: |
|
TEXT |
Shape/type: |
Bulb States:
Bulbs have dynamic states managed by the traffic control system (phase provider):
On: Bulb is illuminatedOff: Bulb is not illuminatedBlinking: Bulb blinks (on/off cycles)
Complete Example
Below is a two-lane road segment using the schema.
Note on geometry encoding: GeoPackage stores geometries as GeoPackageBinary (GPKG BLOBs), not raw WKT. For readability the example show geometries as WKT. In practice, tools like GDAL/OGR, SpatiaLite, or geopackage C++/Python APIs handle this automatically.
Conceptual layout
y = +3.5 ─────────────────── left outer boundary
y = 0.0 ─────────────────── center boundary (shared)
y = -3.5 ─────────────────── right outer boundary
x: 0 ──────────────────────────────── 100
One junction
One segment
Two lanes
Three lane boundaries
Lanes share the center boundary
Junction and segment
INSERT INTO junctions (junction_id, name)
VALUES ('j1', 'Main junction');
INSERT INTO segments (segment_id, junction_id, name)
VALUES ('s1', 'j1', 'Straight segment');
Lane boundaries
We create three lane_boundaries: b_left_outer, b_center and b_right_outer. Adjacency is inferred because two lanes reference the same boundary ID.
-- Left outer boundary
INSERT INTO lane_boundaries (boundary_id, geometry)
VALUES (
'b_left_outer',
-- LINESTRING(0 3.5 1.0, 100 3.5 1.0)
-- In real code: convert WKT → GeoPackageBinary
ST_GeomFromText('LINESTRING(0 3.5 1.0, 100 3.5 1.0)', 0)
);
-- Center boundary (shared)
INSERT INTO lane_boundaries (boundary_id, geometry)
VALUES (
'b_center',
ST_GeomFromText('LINESTRING(0 0.0 1.0, 100 0.0 1.0)', 0)
);
-- Right outer boundary
INSERT INTO lane_boundaries (boundary_id, geometry)
VALUES (
'b_right_outer',
ST_GeomFromText('LINESTRING(0 -3.5 1.0, 100 -3.5 1.0)', 0)
);
Lanes
Two forward-driving lanes:
Lane L1: between left outer and center
Lane L2: between center and right outer
-- Left lane
INSERT INTO lanes (
lane_id,
segment_id,
lane_type,
direction,
left_boundary_id,
left_boundary_inverted,
right_boundary_id,
right_boundary_inverted
)
VALUES (
'lane_1',
's1',
'driving',
'forward',
'b_left_outer',
FALSE,
'b_center',
FALSE
);
-- Right lane
INSERT INTO lanes (
lane_id,
segment_id,
lane_type,
direction,
left_boundary_id,
left_boundary_inverted,
right_boundary_id,
right_boundary_inverted
)
VALUES (
'lane_2',
's1',
'driving',
'forward',
'b_center',
FALSE,
'b_right_outer',
FALSE
);
Optional: lane markings (center dashed line)
INSERT INTO lane_markings (
marking_id,
boundary_id,
s_start,
s_end,
marking_type,
color,
lane_change_rule
)
VALUES (
'center_dashed',
'b_center',
0.0,
100.0,
'dashed',
'white',
'both'
);
Optional: Branch points for connectivity
INSERT INTO branch_point_lanes
(branch_point_id, lane_id, side, lane_end)
VALUES
('bp_start', 'lane_1', 'a', 'start'),
('bp_start', 'lane_2', 'a', 'start'),
('bp_end', 'lane_1', 'b', 'finish'),
('bp_end', 'lane_2', 'b', 'finish');
Optional: Speed limits
-- 50 km/h on both lanes for the full length
INSERT INTO speed_limits (speed_limit_id, lane_id, s_start, s_end, max_speed, description)
VALUES
('sl_lane1', 'lane_1', 0.0, 100.0, 13.89, '50 km/h zone'),
('sl_lane2', 'lane_2', 0.0, 100.0, 13.89, '50 km/h zone');