3

I've got an arcpy.Polyline() object and I have to insert a new vertex in it, which has to be placed on a center of a line. Right now I can calculate the center point coordinates but how to add it to an existing Polyline object in the right place of points order? The existing vertices order should not be changed, so the output line has to have previous vertices plus the new one somehere in the middle.

with arcpy.da.UpdateCursor(line_in,'SHAPE@') as uc:
    for row in uc:
        Midpoint = row[0].positionAlongLine(0.50,True).firstPoint   
        mid_p = arcpy.Point(Midpoint.X,Midpoint.Y)

ArcGIS 10.2 has no segmentAlongLine method so is it possible to update line's vertices here?


Due to the suggestion that this question has been already asked I would like to clarify that I don't need a new layer with points. The goal is to add a vertex to existing line.

  • @ahmadhanb I think it is about creating the output layer with points. I need to change only the line geometry. – Pavel Pereverzev Jul 28 '17 at 9:41
2

It is easy to identify the middle point, so the problem simplifies itself to where the mid_point vertex should be inserted.

You can capitalize in the fact that the mid_point intersects with at least one segment of the original line. The intersection can be checked with the geometry.disjoint (returns boolean) method. If it is False, the two features intersect:

import arcpy

# Create a polyline geometry
array = arcpy.Array([arcpy.Point(459111.6681, 5010433.1285),
                     arcpy.Point(472516.3818, 5001431.0808),
                     arcpy.Point(477710.8185, 4986587.1063)])
polyline = arcpy.Polyline(array)


############# This is the Code you are looking for, you can ignore the above

# Polyline.positionAlongLine returns PointGeometry object, 
# but we need Point Object to add in our array later, hense I use the centroid method.
middle_point = polyline.positionAlongLine(0.5, True).centroid 

# itterate between each segments
vertices = polyline.getPart(0)
for i in xrange(len(vertices) - 1):
    tmp_line = arcpy.Polyline(arcpy.Array(
        [vertices[i], vertices[i + 1]]))
    # The point should be added between these to segments
    if not tmp_line.disjoint(middle_point):
        vertices.insert(i+1, middle_point)
        break

# This is your new Geom to replace the old one
geom = arcpy.Polyline(vertices)


for v in geom.getPart(0):
    print v

>> 459111.6681 5010433.1285 NaN NaN
>> 472341.908022479 5001548.25012586 NaN NaN  # The new point has been inserted correctly
>> 472516.3818 5001431.0808 NaN NaN
>> 477710.8185 4986587.1063 NaN NaN

Edit: Didn't notice the OP did not have access to a critical method, so the following solution is not applicable anymore. But the Algorithm is cool, so I leave it here.

My solution would be to use geometry.segmentAlongLine method which returns a new polyline segment from the designated start until the finish.

So in a sense you build two lines: line_1 from start to mid, line_2 from mid to finish. You unpack the vertices for each line and then you repack them together including the middle vertex:

Here's an example:

import arcpy

# Create a polyline geometry
array = arcpy.Array([arcpy.Point(459111.6681, 5010433.1285),
                     arcpy.Point(472516.3818, 5001431.0808),
                     arcpy.Point(477710.8185, 4986587.1063)])

# The following represents the extracted geom Object
polyline = arcpy.Polyline(array)

leg_1 = polyline.segmentAlongLine(0, 0.5, True)  # from 0% distance, to 50% distance (midway), use percentages
leg_2 = polyline.segmentAlongLine(0.5, 1.0, True)
arr = arcpy.Array()
for p in leg_1, leg_2:
    for _ in p.getPart(0):
        # the if bellow is so we won't include duplicate vertices:
        # leg_1.last_point == leg_2.first_point
        # With each additional vertex the comparison becomes more and more costly.
        # Maybe find a better way to check if the new vertex is already in the Array?
        if not any([x.equals(_) for x in arr]):
            arr.add(_)

# This your new Geom to replace the old one
geom = arcpy.Polyline(arr)

for _ in geom.getPart(0):
    print _
  • thanks for your help however I saw the method called segmentAlongLine and it's not available in ArcGIS 10.2 which I use. But your code will be helpful in future! – Pavel Pereverzev Jul 31 '17 at 6:32
  • The top codr does not use that method. The bottom does. – nickves Jul 31 '17 at 7:38
1

Recently with the piece of advice of Sergei Norin I have found the solution for this case. It is about calculations of distances between vertexes within the line.

with arcpy.da.SearchCursor(line_in,'SHAPE@') as sc:

    for row in sc:
        aa = list(row[0].getPart(0)) # GETTING A LIST OF VERTEXES AS POINTS


        fst = arcpy.Array() # THE ARRAY OF COORDINATES OF THE FIRST HALF OF THE LINE
        snd = arcpy.Array() # THE ARRAY OF COORDINATES OF THE SECOND HALF OF THE LINE

        midpoint = row[0].positionAlongLine(0.50,True).firstPoint   
        mid_p = arcpy.Point(midpoint.X,midpoint.Y) #CREATING THE MIDPOINT OF THE INITIAL LINE

        snd.add(mid_p) # ADDING THE MIDPOINT AS A DEFAULT START POINT IN THE SECOND HALF

        mediana = row[0].getLength()/2 #GETTING THE HALF OF THE INITIAL LINE'S LENGTH

        nu = 0 # THE LENGTH CALCULATING FROM EACH VERTEX FROM THE BEGINNIG OF A LINE
        for i, p in enumerate(aa): #LOOP THROUGH VERTEXES' 
            try:
                ini = i # GETTING THE NUMBER OF INITIAL LOOPING VERTEX
                nex = i+1 # GETTING THE NUMBER OF THE NEXT VERTEX

                # GETTING THEIR COORDINATES

                px0 = aa[ini].X 
                px1 = aa[nex].X
                py0 = aa[ini].Y 
                py1 = aa[nex].Y

                # CALCULATING A DISTANCE BETWEEN INITIAL AND NEXT VERTEXES

                dif = math.sqrt(math.pow((px1-px0),2)+math.pow((py1-py0),2))

                # ADDING VERTEX TO A NEW  COORDIANTE LIST

                poi = arcpy.Point(p.X,p.Y) 
                fst.add(poi)



                nu=nu+dif # SUM DISTANCE BETWEEN VERTEXES WITH A TOTAL LENGTH FROM THE BEGINNIG

                #IN CASE WHEN THE LENGTH OF PREVIOUSLY COUNTED LENGTH IS MORE THAN "MEDIANA" PARAMETER, THIS LOOP ENDS UP WITH ADDING THE REMAINING POINTS TO THE LIST

                if nu>mediana:
                    fst.add(mid_p)
                    for n, j in enumerate(aa):
                        try:

                            px3 = aa[nex].X
                            px4 = aa[nex].Y
                            pei = arcpy.Point(px3,px4)
                            fst.add(pei)
                            nex=nex+1
                        except:
                            pass    


                    #fst.add(mid_p)

                    break
            except:
                    pass


# A NEW LINE IS CREATED AND PLACED 

f_lin = arcpy.Polyline(fst,sr)

icc = arcpy.da.InsertCursor(line_in,'SHAPE@')
icc.insertRow([f_lin])

del icc     

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.