Sunday, 8 April 2018

Extending line by specified distance in ArcGIS for Desktop?


I have a purely aesthetic layer which has arrow symbols. Some aren't showing up right because the line is too small. I have selected maybe 50 records where I need to extend this line by a given number (ex. 2 meters). The extend line tool only extends lines to a specified intersection, so this tool isn't what I'm looking for.



I've tried editing the shape length field but it won't let me. Is there a simple way of doing this via field calculator or within the editor tool bar?



Answer



Well I think I've gotten it down for lines of any vertex count. I haven't attempted multipart lines since I've never messed with it in arcpy. The coding was made a bit more difficult since there isn't write access to the lastPoint property for Geometry objects. Instead of using the slope (which was my initial thought), I used the code from this SO question. It doesn't rely on trigonometry, so it should be slightly more efficient. The following code works by moving the endpoint of a line to a new coordinate that lies along the prolongation of a line from the last two vertices. I tested it on a shapefile.


from math import hypot
import collections
from operator import add
import arcpy

layer = arcpy.GetParameterAsText(0)
distance = float(arcpy.GetParameterAsText(1))


#Computes new coordinates x3,y3 at a specified distance
#along the prolongation of the line from x1,y1 to x2,y2
def newcoord(coords, dist):
(x1,y1),(x2,y2) = coords
dx = x2 - x1
dy = y2 - y1
linelen = hypot(dx, dy)

x3 = x2 + dx/linelen * dist

y3 = y2 + dy/linelen * dist
return x3, y3

#accumulate([1,2,3,4,5]) --> 1 3 6 10 15
#Equivalent to itertools.accumulate() which isn't present in Python 2.7
def accumulate(iterable):
it = iter(iterable)
total = next(it)
yield total
for element in it:

total = add(total, element)
yield total

#OID is needed to determine how to break up flat list of data by feature.
coordinates = [[row[0], row[1]] for row in
arcpy.da.SearchCursor(layer, ["OID@", "SHAPE@XY"], explode_to_points=True)]

oid,vert = zip(*coordinates)

#Construct list of numbers that mark the start of a new feature class.

#This is created by counting OIDS and then accumulating the values.
vertcounts = list(accumulate(collections.Counter(oid).values()))

#Grab the last two vertices of each feature
lastpoint = [point for x,point in enumerate(vert) if x+1 in vertcounts or x+2 in vertcounts]

#Convert flat list of tuples to list of lists of tuples.
#Obtain list of tuples of new end coordinates.
newvert = [newcoord(y, distance) for y in zip(*[iter(lastpoint)]*2)]


j = 0
with arcpy.da.UpdateCursor(layer, "SHAPE@XY", explode_to_points=True) as rows:
for i,row in enumerate(rows):
if i+1 in vertcounts:
row[0] = newvert[j]
j+=1
rows.updateRow(row)

I set the symbology to arrow at end for categories based on OID so that it will be easier to see the separation between features. Labeling was set to count vertices.enter image description here


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...