How to use ArcPy Geometry MeasureOnLine

3919
3
Jump to solution
08-14-2014 12:50 PM
PeterWilson
Occasional Contributor III

I've found the following forum post https://community.esri.com/thread/102954 where the geometry method measureOnLine was used. I'm trying to determine how to use the same method for calculating the chainage (accumulated distance) along a cross-section polyline. I need to extract the profile of each cross-section into a point feature class based on the position of the vertices within each cross-section polyline. I would like to end up with a point feature class with the following fields:

  • ID (Cross-Section)
  • Z (Elevation)
  • Chainage (Distance m from start of polyline)

The chainage will be the accumulative distance (i.e. XS point 1 = 0 ; XS point 2 = 20m ; XS point 3 = 35m etc)

I would like to use the polyline Z Feature Class to generate a pointGeometry Object and iterate through each point that will be used as the point for the measureOnLine method against the original Polyline Feature Class. The determined length (Chainage) will be inserted (i.e. InsertCursor) with the Point Object ; ID (Cross Section Field) and Chainage into a newly generated feature class.

I'm still very new to ArcPy and Python and battling to figure out how to iterate through the PointGeometry from the Polyline to use the measureOnline method and then insert the Point Geometry ; ID ; Chainage into a new Feature Class.

Any help in getting going will trully be appreciated.

Regards

Peter Wilson

0 Kudos
1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

Hi Peter,

Since you are interested in Python, let's throw in some Python code. I haven't tested the code, since the measureOnLine method was introduced at 10.2.1 and I have a lower version here. The rest of the code seems to work though.

Have a look at the code below:

  • Lines 5 and 6 define the path to you input 3D lines and output points (which will be created in the process)
  • Lines 9 to 12 define the column names. fld_ID should point at an existing field in you input featureclass, which will be transferred to the output points
  • Lines 18 and 19 read some properties of the input featureclass, used to create the output pointfeatureclass and the ID field
  • Lines 22 till 28 will create the empty point featureclass
  • Lines 31 till 34 will add the output fields to the point featureclass
  • lines 40 and 42 define the insert cursor (points) and search cursor (lines)
  • Rest of the lines perform a loop through the points of each part of the polyline, and determine the values you're interested in

Hope it works for you, good luck, Xander

import arcpy, os

arcpy.env.overwriteOutput = True

# featureclasses

fc_in = r"D:\Xander\GeoNet\3Dlines2Points\test.gdb\my3Dlines"

fc_out = r"D:\Xander\GeoNet\3Dlines2Points\test.gdb\myPoints"

# fields

fld_ID = "ID" # ID of Cross-Section in line featureclass

fld_Number = "Number" # just to add a number to the points

fld_Z = "Z" # Elevation

fld_Chainage = "Chainage" # Distance m from start of polyline

# fields for search cursor on input lines

flds_in = ("SHAPE@", fld_ID)

# get some input data to create the output

sr = arcpy.Describe(fc_in).spatialReference

ofld_ID = arcpy.ListFields(fc_in, fld_ID)[0]

# create the output featureclass

geometry_type = "POINT"

template = ""

has_m = "DISABLED" # you could enable M values...

has_z = "ENABLED"

ws_path, fc_out_name = os.path.split(fc_out)

arcpy.CreateFeatureclass_management(ws_path, fc_out_name,

    geometry_type, template, has_m, has_z, sr)

# add the fields to the point featureclass

arcpy.AddField_management(fc_out, fld_ID, ofld_ID.type)

arcpy.AddField_management(fc_out, fld_Number, "LONG")

arcpy.AddField_management(fc_out, fld_Z, "DOUBLE")

arcpy.AddField_management(fc_out, fld_Chainage, "DOUBLE" )

# fields for insert cursor on output points

flds_out = ("SHAPE@", fld_ID, fld_Number, fld_Z, fld_Chainage)

# start insert cursor for output points

with arcpy.da.InsertCursor(fc_out, flds_out) as curs_out:

    # start search cursor on lines

    with arcpy.da.SearchCursor(fc_in, flds_in) as curs:

         for row in curs:

            number = 0

            polyline = row[0]

            line_ID = row[1]

            for part in polyline:

                for pnt in part:

                    number += 1

                    if pnt:

                        ptGeom = arcpy.PointGeometry(pnt, sr)

                        chainage = polyline.measureOnLine(ptGeom)

                        elevation = pnt.Z

                        curs_out.insertRow((ptGeom, line_ID, number, elevation, chainage))

View solution in original post

0 Kudos
3 Replies
RiyasDeen
Occasional Contributor III
  1. Use feature vertices to point tool for converting all vertices of your polyline to point feature class ArcGIS Desktop
  2. Use Add XY Coordinates tool ArcGIS Desktop‌. This will add XY coordinates as well as Z values.
  3. Use search cursor to iterate through point FC created in step 2.
  4. Use ORIG_FID on the point feature class to search your polyline,
  5. Use Point FC shape as point input for measureOnLine method. You got your chainage here.
  6. Update your Point feature with chainage.

Alternatively:

  1. Use Create routes tool to create an M aware polyline from your Z polyline. ArcGIS Help 10.1‌, set measure source as length.
  2. Now your polyline has both Z and M (which is distance from start of the line)
  3. Use feature vertices to point tool for converting all vertices of your polyline to point feature class ArcGIS Desktop
  4. Use Add XY Coordinates tool ArcGIS Desktop‌. This will add XY coordinates as well as Z and M values.
  5. You got what you wanted, all these can be done through model builder and you export it out as python script if you want.
0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi Peter,

Since you are interested in Python, let's throw in some Python code. I haven't tested the code, since the measureOnLine method was introduced at 10.2.1 and I have a lower version here. The rest of the code seems to work though.

Have a look at the code below:

  • Lines 5 and 6 define the path to you input 3D lines and output points (which will be created in the process)
  • Lines 9 to 12 define the column names. fld_ID should point at an existing field in you input featureclass, which will be transferred to the output points
  • Lines 18 and 19 read some properties of the input featureclass, used to create the output pointfeatureclass and the ID field
  • Lines 22 till 28 will create the empty point featureclass
  • Lines 31 till 34 will add the output fields to the point featureclass
  • lines 40 and 42 define the insert cursor (points) and search cursor (lines)
  • Rest of the lines perform a loop through the points of each part of the polyline, and determine the values you're interested in

Hope it works for you, good luck, Xander

import arcpy, os

arcpy.env.overwriteOutput = True

# featureclasses

fc_in = r"D:\Xander\GeoNet\3Dlines2Points\test.gdb\my3Dlines"

fc_out = r"D:\Xander\GeoNet\3Dlines2Points\test.gdb\myPoints"

# fields

fld_ID = "ID" # ID of Cross-Section in line featureclass

fld_Number = "Number" # just to add a number to the points

fld_Z = "Z" # Elevation

fld_Chainage = "Chainage" # Distance m from start of polyline

# fields for search cursor on input lines

flds_in = ("SHAPE@", fld_ID)

# get some input data to create the output

sr = arcpy.Describe(fc_in).spatialReference

ofld_ID = arcpy.ListFields(fc_in, fld_ID)[0]

# create the output featureclass

geometry_type = "POINT"

template = ""

has_m = "DISABLED" # you could enable M values...

has_z = "ENABLED"

ws_path, fc_out_name = os.path.split(fc_out)

arcpy.CreateFeatureclass_management(ws_path, fc_out_name,

    geometry_type, template, has_m, has_z, sr)

# add the fields to the point featureclass

arcpy.AddField_management(fc_out, fld_ID, ofld_ID.type)

arcpy.AddField_management(fc_out, fld_Number, "LONG")

arcpy.AddField_management(fc_out, fld_Z, "DOUBLE")

arcpy.AddField_management(fc_out, fld_Chainage, "DOUBLE" )

# fields for insert cursor on output points

flds_out = ("SHAPE@", fld_ID, fld_Number, fld_Z, fld_Chainage)

# start insert cursor for output points

with arcpy.da.InsertCursor(fc_out, flds_out) as curs_out:

    # start search cursor on lines

    with arcpy.da.SearchCursor(fc_in, flds_in) as curs:

         for row in curs:

            number = 0

            polyline = row[0]

            line_ID = row[1]

            for part in polyline:

                for pnt in part:

                    number += 1

                    if pnt:

                        ptGeom = arcpy.PointGeometry(pnt, sr)

                        chainage = polyline.measureOnLine(ptGeom)

                        elevation = pnt.Z

                        curs_out.insertRow((ptGeom, line_ID, number, elevation, chainage))

0 Kudos
XanderBakker
Esri Esteemed Contributor

I was just thinking, I don't need the 10.2.1 functionality to do this. It is also possible to something like this:

# start insert cursor for output points

with arcpy.da.InsertCursor(fc_out, flds_out) as curs_out:

    # start search cursor on lines

    with arcpy.da.SearchCursor(fc_in, flds_in) as curs:

         for row in curs:

            number = 0

            polyline = row[0]

            line_ID = row[1]

            chainage = 0

            for part in polyline:

                for pnt in part:

                    number += 1

                    if pnt:

                        ptGeom = arcpy.PointGeometry(pnt, sr)

                        if number > 1:

                            chainage += ptGeom.distanceTo(prev_pnt)

                        # chainage = polyline.measureOnLine(ptGeom)

                        elevation = pnt.Z

                        curs_out.insertRow((ptGeom, line_ID, number, elevation, chainage))

                        prev_pnt = ptGeom

It uses the distanceTo on the point geometry to sum the traveled distance on the line...

Kind regards, Xander

0 Kudos