# How to create an offset polyline in arcpy?

I am trying to create an offset polyline using arcpy, identical to the function in ArcObjects (constructoffset). After trying to access ArcObjects in arcpy, which I found difficult for those not well-versed in COM languages, and after much searching for functionality in the regular geoprocessing toolboxes, I discovered that this functionality seems to only exist in ArcObjects. There may be ways to program this functionality in arcpy using trig, but that seems really hard. Essentially, the product I'd like is an offset polyline that preserves all original geometry and curvature. Simply using a SearchCursor on the input and building a new feature class based on its geometry requires some algorithm I don't know. This is because without an algorithm based on slope, etc., a one-size-fits-all addition or subtraction will not preserve an offset shape for all slopes. Any help?

• When you mean slope, do you mean that you have a PolyLineZ feature class with Z values at each vertex? And if so, how would offsetting the line affect the Z values? Wouldn't simply preserving them be what you want? Commented May 20, 2012 at 22:58
• Also are your features in projected coordinates? If so you could simply add/subtract your offset to each vertex coordinate. Commented May 20, 2012 at 23:07
• This doesn't work because if my offset is 5 meters, I would need to have that 5 meter offset extend from each line perpendicular from any point on the line, else a 5 meter x offset on a horizontal line just moves the whole line west 5 meters, as opposed to extending in the y-direction as well. Therefore, the offset calculation is more complex than that, and I think it's what the Buffer function does. Commented May 22, 2012 at 18:58

We came up with this technique at work that uses geoprocessing tools to do the job:

First off, make sure your line segs have unique IDs.

1. Buffer "FLAT" in each direction of the line, add a new field of the same name to each and give them a direction ("L","R").
2. Merge the buffers together.
3. Buffer the original line again, this time "FULL".
4. Convert the FULL buffer to lines (PolygonToLine: "IGNORE_NEIGHBORS"), then convert its vertices to points (FeatureVerticesToPoints: "BOTH_ENDS").
5. Buffer each point by a very small width (e.g. 0.1 m).
6. Split the lines by vertex (SplitLine).
7. Select by location to select all lines that intersect with the point buffers.
8. Delete these lines.
9. Dissolve ("SINGLE_PART") on the segment unique ID.
10. Spatial Join ("SHARE_A_LINE_SEGMENT_WITH") the new lines to the merged "LEFT" and "RIGHT" buffers to attribute them with direction.

Voila! Offset lines without ArcObjects.

Here is the code to do the same in arcpy

``````import arcpy

tmpwrkspace = "C:/tempgdb/temp.gdb/"
fc_line = tmpwrkspace + "MultiLineLayer"

# Set some variables
tempFeatureClass = tmpwrkspace + "temp_pline"

#set the offset to +n or -n for direction
xOffset = 0.2
yOffset = 0.2

if arcpy.Exists(tempFeatureClass):
arcpy.Delete_management(tempFeatureClass)

arcpy.CopyFeatures_management(fc_line, tempFeatureClass)

# Perform the move
with arcpy.da.UpdateCursor(tempFeatureClass, ["SHAPE@XY"]) as cursor:
for row in cursor:
cursor.updateRow([[row[0][0] + xOffset, row[0][1] + yOffset]])
``````
• this takes offset as a decimal degree. is there any to input offset distance in meters. lets say i want to take offset around 20 meters of Line ?? Commented Mar 10, 2016 at 8:57
• It depend on liner unit in projection system. If liner unit is meter then it will take meter. Commented Mar 10, 2016 at 9:48
• isn't there any way to forcefully give desired unit into offset ? Commented Mar 10, 2016 at 10:04
• check this Linear unit and to change projection system define projection. Commented Mar 10, 2016 at 10:17
• I can't get this to work, it offsets my entire feature class. Also I have a grid of lines (street centerlines) so I would assume the X and Y offset would vary based on the orientation of the line? also I have varied distances for each line, I have the values stored in an attribute field. I tried to pull those using a search cursor wiith the original layer with the updateCursor nested below. Commented Apr 18, 2016 at 21:24