Stationing and offset is a simple and precise method of locating and capturing information, typically during a construction project, along linear assets like roads, railways, or pipelines.
Stationing is the measurement of distance along a designated centerline or alignment. In ArcGIS, stationing is a measurement value (m-value) that is dynamically associated with a linear feature or route in an Linear Referencing System (LRS). Offset is the perpendicular distance from the centerline to a specific point – like where the mobile worker is capturing information.
Within a civil construction project, stationing and offset information is captured as part of specific inspection tasks to ensure roads or pipelines are built according to design specifications. Stationing provides reference and when combined with offset, provide accuracy of measurements.
Now, with ArcGIS Field Maps, you can automatically capture stationing and offset values along with other route characteristics while filling out inspection form details. Paired with an external GNSS receiver, referenced locations can be accurate to the cm. If a design surface is provided, Field Maps can also validate that current construction is “at grade” with design. Please read our earthworks blog article for how to configure Field Maps to capture cut and fill measurements.
In this article, we’ll show you how to configure your map to capture these important attributes. Not included below is the expectation that you already have a horizontal alignment published as a linear feature layer that includes m-values. This may be a route stored and managed using ArcGIS Roads and Highways or ArcGIS Pipeline Referencing, or an alignment that has been imported from a CAD drawing using ArcGIS Pro.
For step-by-step instructions on how to add expressions to the form in Field Maps Designer, see Add calculated expressions. These expressions utilize the PointToCoordinate function available in the latest release of ArcGIS Field Maps.
Add Alignment Form Elements
Using Field Maps Designer, start by adding these attributes and form elements to your form:
- Station – Field type: String, Field name: Station, Input type: Text – single line
- Offset (ft) – Field type: String, Field name: RelOffset, Input type: Text – single line
- RouteID – Field type: String, Field name: RouteID, Input type: Text – single line
- Measure (mile) – Field type double, Field name: Measure, Input type: number – double
Calculated expressions
With the above form elements added, follow the directions below to include calculated expressions that will reference values from your horizontal alignment feature layer.
Station field: get road alignment station
//The measure value is in miles, and it is converted to feet
var station = $feature.Measure*5280;
var IsNegative = 0 //tracks +/- values
var Type = ‘Feet’ //”Meters” Switch type as needed
if (station < 0) {
//convert all negative stations to positive and append back the – at the end
station = station * –1
IsNegative = 1
}
//##00+00.0 or ##0+000.0 – These functions will provide this format regardless the length of the field
//formatting with decimals after the + sign – if there is not
if (Type == ‘Feet‘) var StationlbR = Text(station, ‘00.0‘)
if (Type == ‘Meters’) var StationlbR = Text(station, ‘000.0‘)
//Getting rid of the two initial character dividing by 100 or 1000. formatting result with 2 zeros and room for more characters
//using the Split function to extract from the second character from the left of the decimal point
if (Type == ‘Feet‘)
var StationlbL = Text(First(Split(station / 100, ‘.’, 1)), ‘###00‘)
if (Type == ‘Meters‘)
var StationlbL = Text(First(Split(station / 1000, ‘.’, 1)), ‘###000‘)
var Stationlb = Concatenate([StationlbL, StationlbR], ‘+‘)
if (IsNegative == 1) Stationlb = ‘-‘ + Stationlb
return Stationlb
Offset (ft) field: get road alignment offset distance (geodetic)
var roads = FeatureSetByName($map, “AllRoutes“)
var nearestFeature = First(Intersects(roads, BufferGeodetic($feature, 10, “meter“)));
//Get the nearest point coordinate in the road alignment
var m_point = PointToCoordinate(nearestFeature, $feature).coordinate
//Get the geodetic distance between point feature and the nearest point coordinate
var offsetGeodeticDis_ft = Round(distanceGeodetic($feature, m_point, ‘feet‘),2) //Return geodetic distance
return offsetGeodeticDis_ft
RouteID field: get the route identifier field value of linear referenced feature
//Get the routes layer
var roads = FeatureSetByName($map, “AllRoutes“)
//Find the nearest route feature
var nearestFeature = First(Intersects(roads, BufferGeodetic($feature, 10, “meter“)));
//Return Route ID
return nearestFeature.RouteID
Measure (miles): Get measure m-value in miles
//Get the routes layer
var roads = FeatureSetByName($map, “AllRoutes“)
//Find the nearest route feature
var nearestFeature = First(Intersects(roads, BufferGeodetic($feature, 10, “meter“)));
// Find the nearest coordinate’s M value of the search point feature to the route
var closestM= Round(PointToCoordinate(nearestFeature, $feature).coordinate.m,4)
// Return measure M-value. It uses miles
return closestM
After constructing the form and including the horizontal alignment in your map, all that’s left is to test and deploy your new stationing and offset solution!
In a related article, learn how you can capture elevation values in Field Maps and compare the elevation from your GNSS receiver to a surface model. Combining stationing and offset with earthwork capture can streamline construction inspection workflows.
Do you have to have ArcGIS Roads and Highways or ArcGIS Pipeline Referencing, to use this in AGOL?
Can you make a m-ware line in Pro using LR tools and use that?
You can do this with any m-enabled line. Roads and Highways is not necessary.
Note that a tricky case not covered here is when you have two alignments close together, for example when you have crossing alignments, and the feature should get the m value of alignment A, even if it’s physcially closer to alignment B.
You don’t want new point or line feature to pick up the wrong alignment. The setup given in the article is that the buffer in the field calc grabs the closest alignment to the feature.
So what can you do?
One possibility is filtering out other alignments before collecting for a specific one. It is a bit of a hassle for folks in the field.
The option that I ended up using was building the feature classes (lines and points) to be collected with relates to the alignment feture class before publishing them to our AGOL, then setting up fieldmaps so that the user first selecst the alignment they want to collect against and then creates the feature as a related item. That way the field calculation can just grab the related alignment using the FeatureSetByRelationshipName function (it would replace the Buffer function in the var nearestFeature variable in the Measure Miles calculation.
I had to build a custom PointToCoordinate function at the time (2024) because though the function existed in arcade and was usable in a web map, the version of arcade in the field maps app did not have the function in its profile. I’m glad that they added it. It will make this esier.