Select to view content in your preferred language

Create Points Along Line at Given Interval

802
10
10-31-2025 09:10 AM
KevinPiraino2
Frequent Contributor

Hello, I'm hoping that someone smarter than me can possibly assist me devising a code snippet to generate points along a line (specifically a "route" line; a line with M values) at a given interval (100 ft). I am basically trying to replicate the labeling from "route" line layer that is visible in ArcGIS Pro using point features so that I may use the point features in ArcGIS Online (as it seems M aware features/functionality in AGOL are not supported). 

I know the GP tool "Generate Points Along Line" currently performs this action and works well for a portion of my data where the initial M value (stationing) is a whole number divisible by 100 (EX: 41700, 86000, etc.). Where the issue occurs though is when the initial M value (stationing) is NOT divisible by 100 (41618, 87224, etc.). Using the GP tool causes an offset from the desired "100 ft interval" due to the initial M value (stationing) not starting at a value divisible by 100. Below screenshot was created using existing GP tool.

Basically, when the initial M value is divisible by 100, sequence would read for example: 41700, 41800, 41900, etc. But, when the initial M value is NOT divisible by 100, sequence would read for example: 41618, 41718, 41818, etc. How can I create points at the beginning and end of line as well as at every 100ft interval where the M value (stationing) of the point is divisible by 100. For example ideal output stationing: 41618, 41700, 41800, 41900,...,45000,45100,45121.

KevinPiraino2_0-1761926823115.png

 

 

Tags (3)
0 Kudos
10 Replies
HaydenWelch
MVP Regular Contributor

That's super odd. I know that Cursors use numpy on the backend so I wonder if they are casting to a smaller ctype during that operation. Either way, thanks for clarifying. Gonna investigate this more and see if it is indeed a bug with the type casting of a float during the cursor operation.

 

Edit, it's not a bug, I'm just stupid and need to stop always using the range object for numerical iterations:

HaydenWelch_0-1762284061667.png

Only change I had to make was capturing the fractional part of the modulo operation to then add back to the measure in the primary loop:

import arcpy
def get_measures(in_lines: str, out_points: str, measure: int=100, ref: arcpy.SpatialReference|None=None):
    with(
        arcpy.da.SearchCursor(in_lines, ['OID@', 'SHAPE@'], spatial_reference=ref) as line_cur, 
        arcpy.da.InsertCursor(out_points, ['OID@', 'SHAPE@']) as point_cur
    ):
        for oid, line in line_cur:
            # Reveal types
            oid: int
            line: arcpy.Polyline
            # Make sure that the fort and last points inherit M values from the line
            fp = arcpy.PointGeometry(line.firstPoint, has_m=True, has_z=True, spatial_reference=line.spatialReference)
            lp = arcpy.PointGeometry(line.lastPoint, has_m=True, has_z=True, spatial_reference=line.spatialReference)
            # Insert first point
            point_cur.insertRow((oid, fp))
            # Get the first even measure from the offset point
            first_meas = measure - (line.firstPoint.M or measure) % measure
            # Store the fractional part
            fraction_part = first_meas % 1
            # Iterate over the integer offset, adding the fractional offset to each measure
            for m in range(int(first_meas), int(line.length), measure):
                point_cur.insertRow((oid, line.positionAlongLine(m + fraction_part)))
            # Insert the last point
            point_cur.insertRow((oid, lp))

 

Sorry for leading you on a wild goose chase!

 

Edit 2:

There is actually still some floating point error in the values! It seems to happen when a line feature is traveling at an oblique angle to the planar system's grid:

HaydenWelch_1-1762285059758.png

I was able to reduce it down quite a bit by pulling the value directly from the _arc_object of the Point instead of letting arcpy convert it to a double first. Also used the builtin Decimal class for more strictly accurate decimal operations. It would be nice if arcpy would use Decimal by default so there isn't precision loss during the conversion to a float/double.

0 Kudos