Linear Referencing
Linear referencing is the practice of locating observations, measurements, or events along a route using a distance-based measure rather than absolute coordinates. A road asset database might record a pothole at 2.4 km from the start of route R-12; a utility corridor might flag a pressure anomaly at 847 m along a pipeline. Whitebox Next Gen provides the tools to build measured routes, locate observations against them, place events from tables or spatial layers, and — with a Pro licence — audit the consistency and governance of large linear asset datasets.
Core Concepts
A linear-referencing workflow has three parts:
- Routes — line features that define the measurement axis. Each route has a unique identifier and carries M-values (measures) representing cumulative distance from its start point.
- Measures — the distance value used to locate a position along a route.
- Events — point or line observations located by (route_id, measure) or (route_id, from_measure, to_measure) pairs.
Common Whitebox Next Gen applications include:
- road-pavement condition assessment by segment
- pipeline corridor integrity monitoring
- trail difficulty and visibility reporting
- environmental sampling along survey transects
- GPS probe data quality control and kilometrage reporting
Route Calibration and M-Value Management
Measures are only useful when anchored to real-world control points. If your raw routes lack calibration, or have been edited, use the calibration tools to establish stable, field-verified measures.
Initial Calibration from Control Points
route_calibrate() establishes measure values on routes using control points
with known measures. For example, if you have kilometre posts at known
distances along a highway, calibration ensures your event locations align with
field reality.
import whitebox_workflows as wbw
wbe = wbw.WbEnvironment()
wbe.working_directory = '/data/linear_referencing'
routes = wbe.read_vector('highway_centerlines.shp')
km_posts = wbe.read_vector('km_post_locations.shp') # with ROUTE_ID and KNOWN_MEASURE fields
calibrated = wbe.vector.linear_referencing.route_calibrate(
routes=routes,
control_points=km_posts,
control_measure_field='KNOWN_MEASURE',
route_id_field='ROUTE_ID',
snap_tolerance=10.0 # max control-point offset from route (meters)
)
wbe.write_vector(calibrated, 'highway_calibrated.shp')
# Output includes FROM_MEASURE and TO_MEASURE fields.
Recalibration After Edits
If you edit routes (split, merge, or regeometrize), use route_recalibrate()
to scale measures proportionally and maintain event alignment.
edited_routes = wbe.read_vector('highway_edited.shp') # after geometric changes
recalibrated = wbe.vector.linear_referencing.route_recalibrate(
original_routes=calibrated, # reference with valid measures
edited_routes=edited_routes,
route_id_field='ROUTE_ID'
)
wbe.write_vector(recalibrated, 'highway_recalibrated.shp')
Validating Event Snapping
Before placing events on routes, diagnose snapping issues with snap_events_to_routes().
This reports snap distance, offset, and any unmatched events.
obs_points = wbe.read_vector('field_observations.shp')
diag = wbe.vector.linear_referencing.snap_events_to_routes(
routes=calibrated,
events=obs_points,
max_offset_distance=15.0
)
wbe.write_vector(diag, 'observations_snap_diagnostics.shp')
# Output includes ROUTE_ID, MEASURE, and OFFSET fields; unmatched features are excluded.
Step 1 — Understand Your Route Geometry
Routes must be single-part polylines with a consistent digitizing direction. Before dropping events, confirm:
- Each route has a unique identifier stored in a field (e.g.
ROUTE_ID). - No route self-intersects.
- Routes that form a corridor are merged into one feature per route identifier.
Use snap_endnodes and merge_line_segments to clean ragged street-centreline
inputs before treating them as routes.
Step 2 — Locate Points Along Routes
locate_points_along_routes() takes an existing point layer and finds the
nearest position on each matching route, writing back the M-value (measure)
for every point. This is the first tool to reach for when your field team has
collected GPS observation points and you need to convert them to route-distance
offsets for further analysis.
import whitebox_workflows as wbw
wbe = wbw.WbEnvironment()
wbe.working_directory = '/data/linear_referencing'
routes = wbe.read_vector('roads_measured.shp')
obs_points = wbe.read_vector('field_observations.shp')
located = wbe.vector.linear_referencing.locate_points_along_routes(
routes=routes,
points=obs_points,
max_offset_distance=15.0 # max perpendicular snap distance in map units
)
wbe.write_vector(located, 'observations_located.shp')
# Output adds ROUTE_ID, MEASURE, and OFFSET fields to every input point.
The MEASURE field in the output is the along-route distance from the route
start. OFFSET is the perpendicular distance from the point to the route.
Points beyond max_offset_distance are not matched and are excluded from the
output.
Step 3 — Place Events from a Table
Point Events
route_event_points_from_table() reads a CSV (or other tabular file) where
each row specifies a route identifier and a measure value, and places a point
feature at that position along the matching route. This is the standard import
path for lab results, inspection records, or maintenance logs stored in
external databases.
# events.csv columns: ROUTE_ID, MEASURE, SEVERITY, NOTES
pts = wbe.vector.linear_referencing.route_event_points_from_table(
routes=routes,
events='pavement_defects.csv',
event_route_field='ROUTE_ID',
measure_field='MEASURE'
)
wbe.write_vector(pts, 'pavement_defects_located.shp')
Line (Interval) Events
route_event_lines_from_table() works the same way but reads FROM_MEASURE
and TO_MEASURE columns to produce line segments — useful for pavement
condition ratings, speed zones, or any attribute that applies to a stretch of
route rather than a single point.
# condition.csv columns: ROUTE_ID, FROM_MEASURE, TO_MEASURE, IRI, CONDITION
segs = wbe.vector.linear_referencing.route_event_lines_from_table(
routes=routes,
events='pavement_condition.csv',
event_route_field='ROUTE_ID',
from_measure_field='FROM_MEASURE',
to_measure_field='TO_MEASURE'
)
wbe.write_vector(segs, 'pavement_condition_segments.shp')
Step 4 — Place Events from a Spatial Layer
When your event data is already a vector layer rather than a plain table, use
the _from_layer variants. These carry across all attributes of the source
feature and can optionally write the feature's original FID and XY coordinates
into the output.
Point Events from a Layer
inspections = wbe.read_vector('manhole_inspections.shp')
pts_from_layer = wbe.vector.linear_referencing.route_event_points_from_layer(
routes=routes,
events=inspections,
event_route_field='ROUTE_ID',
measure_field='MEASURE',
write_event_fid=True, # retain original feature ID in output
write_event_xy=True # also store original XY in output
)
wbe.write_vector(pts_from_layer, 'manholes_on_routes.shp')
Line Events from a Layer
speed_zones = wbe.read_vector('speed_zone_events.shp')
segs_from_layer = wbe.vector.linear_referencing.route_event_lines_from_layer(
routes=routes,
events=speed_zones,
event_route_field='ROUTE_ID',
from_measure_field='FROM_M',
to_measure_field='TO_M',
write_event_fid=True
)
wbe.write_vector(segs_from_layer, 'speed_zones_on_routes.shp')
Step 5 — Linear Asset Governance (Pro)
route_event_governance_for_linear_assets audits a complete linear asset
dataset for measure gaps, overlaps, duplicate events, orphaned route
references, and out-of-range measures. It produces a flagged event output and
a structured report — essential for maintaining the integrity of a production
road or utility asset database.
result = wbe.run_tool(
'route_event_governance_for_linear_assets',
{
'routes': 'roads_measured.shp',
'events': 'pavement_condition.shp',
'route_id_field': 'ROUTE_ID',
'from_measure_field': 'FROM_MEASURE',
'to_measure_field': 'TO_MEASURE',
'flagged_output': 'event_flags.shp',
'report': 'governance_report.csv'
}
)
print(result)
The flagged output marks each event with an error code and description. The report CSV summarises error counts by class and route, ready for integration into a maintenance management system.
Note: This tool requires a
WbEnvironmentinitialised with a valid Pro licence.
Complete Workflow: Road Pavement Assessment
The following example takes a road network, locates inspection points collected in the field, overlays a condition rating table, and exports both a point layer and a segment layer ready for pavement management reporting.
import whitebox_workflows as wbw
wbe = wbw.WbEnvironment()
wbe.working_directory = '/data/pavement_assessment'
routes = wbe.read_vector('roads_measured.shp')
field_gps = wbe.read_vector('field_inspection_gps.shp')
# Step 1: Snap GPS observation points onto routes and extract M-values.
located = wbe.vector.linear_referencing.locate_points_along_routes(
routes=routes,
points=field_gps,
max_offset_distance=10.0
)
wbe.write_vector(located, 'gps_on_routes.shp')
# Step 2: Place point defect records from the inspection database.
defects = wbe.vector.linear_referencing.route_event_points_from_table(
routes=routes,
events='defect_records.csv',
event_route_field='ROUTE_ID',
measure_field='MEASURE'
)
wbe.write_vector(defects, 'defects_located.shp')
# Step 3: Place condition rating intervals from the same database.
condition = wbe.vector.linear_referencing.route_event_lines_from_table(
routes=routes,
events='condition_ratings.csv',
event_route_field='ROUTE_ID',
from_measure_field='FROM_M',
to_measure_field='TO_M'
)
wbe.write_vector(condition, 'condition_segments.shp')
# Step 4 (Pro): Audit the condition layer for gaps and overlaps.
result = wbe.run_tool(
'route_event_governance_for_linear_assets',
{
'routes': 'roads_measured.shp',
'events': 'condition_segments.shp',
'route_id_field': 'ROUTE_ID',
'from_measure_field': 'FROM_M',
'to_measure_field': 'TO_M',
'flagged_output': 'condition_flags.shp',
'report': 'governance_report.csv'
}
)
print('Governance report:', result)
Tips
- Routes must have a consistent digitizing direction. If your source network
was assembled from multiple editors, run
snap_endnodesand check that all segments in a single route are digitized in the same direction before computing M-values. locate_points_along_routes()excludes points that exceedmax_offset_distance. Inspect unmatched points to identify GPS outliers or route coverage gaps.- Use
route_event_points_from_table()androute_event_lines_from_table()for bulk imports from asset management databases where the event locations are already stored as route+measure pairs. - Use the
_from_layervariants when you have existing vector event layers that already carry a route identifier and measure fields. - The
route_event_governance_for_linear_assetsPro tool scales to production road-asset databases with millions of events and produces actionable error reports ready for integration into maintenance workflows.