# Creating a line that goes through two point points using python (used for creating a utility line where there are two pole structures)

I am starting up an enterprise GIS program at the Utility company I work for. We are a Generation and Transmission company. Our data was bad when I got here, it was in NAD27 in a .dwg and was off by at least 10 meters.

After a lot of hard work we got all the poles rectified and imported into a Geodatabase so they match aerial photography so everything lines up in ArcGIS Portal. Lines had already been created in the same coordinate system, but they too are considerably off. We can use the point to line geoprocessing tool, but there is a problem. We have a lot of what are called H-frame structures (they usually are higher voltage transmission lines) and each pole has an entry in our oracle database as they are considered separate structures. So we will have a pole 0222 and pole 0222A, about 15 feet apart depending on the construction. If they were a single pole structure I could the point to line tool and everything would be peachy. But because we have the two pole structures when we run the tool we end up with something that looks like this.

We, of course, don't want this zig zag type of line and want it to run between the two structures so it looks like this

What I'm trying to do then is write a script where it goes through the entire pole dataset (we have 30,000+ poles so drawing them in is out of the question) and selects a pole that is within 50ft (the span between each structure is much longer than that so it will select the other pole in the structure). I have it set to calculate the UTM coordinates of the structure adds the northings and eastings together and divides by 2 to find the point in the center between them. I suppose I could also tell it to draw a line in_memory and calculate the midpoint, but my approach seems more simple (maybe it isn't). Anyway, at this point I'm stuck using a for loop with the search cursor to select the line and just print out the easting and northing of the lines, then I will move on to the math part and the creating lines based on those midpoints. I had someone here who knows a lot more about python take a look at my code, but he wasn't familiar with the arcpy library so he said he couldn't help much.

This is the point that I'm stuck at.

I'm pretty rusty on my python and am not a programmer by trade.

#This Script will convert a series of poles to lines#
#Power Co-op. Inc. Format"
#Peter#
#12 October 2017"

#Import Modules
import arcpy
from arcpy import env
import os
import math
import numpy

#Set Workspace

arcpy.env.workspace = "in_memory"

#set Parameters#
fc =r"P:\Scripts\pole_2_line\demo_data.gdb\demo_poles_short"
fc_layer = arcpy.MakeFeatureLayer_management (fc, 'demo_poles')
pole = "POLE_NUMBE"
line = "LINE_NUMBE"
coordinates = ['SHAPE@XY']

with arcpy.da.SearchCursor (fc_layer, coordinates) as cursor:
for row in cursor:
print row
near_pole = arcpy.SelectLayerByLocation_management (fc_layer, 'WITHIN_A_DISTANCE',fc_layer,"50 feet", "NEW_SELECTION","NOT_INVERT")
print near_pole

• No need to script, can be done through gui if numbering of points is consistent, eg 0023 group is followed by 0024 couple etc. – FelixIP Oct 12 '17 at 21:30
• What level license do you have? – Emil Brundage Oct 12 '17 at 21:36
• Advanced. The numbering is consistent but it needs to between the H frame structure, since they are labeled as xxxx and xxxxA it creates a zig zag line instead of a line between the poles. Unless there is a function that I am missing. It assumes that 0001A comes after 0001 and so forth. The A denotes that it is the second pole of the structure. We could ignore the A pole but then it would go along the side of the structure. In a lot of cases the point to line function works because it is a single pole structure but not in the case of the H frames. – Pete Oct 12 '17 at 21:41
• If pole 74 and 74A are to be aggregated why not use the 'root number' (in this case 74) as an identifier, buffer, dissolve by root number and use the centroid... sort ascending by root number in a table with X,Y and point to line, but before you do that explode multi part buffers and look for any instances of root number > 1 manually. this may be an oversimplification as I haven't seen all your data. That doesn't help with the script but unless there's more serious problems in your data it's a lot of effort for something that could possibly be done with existing tools. – Michael Stimson Oct 12 '17 at 22:09
• Something that is worth considering specifically from the utility GIS perspective: having the line connected to a pole may be important for network analysis, especially if you ever want to get into tracing, or connect fuses/switches/transformers/etc. into the network. Since you're only just starting to get this data into GIS, that consideration may be quite far in the future and not worth your time now -- it just came to mind since we're currently transitioning to Esri's Utility Network, and connectivity (visual and actual) is a regular issue for us. – Erica Oct 12 '17 at 22:27

Cursors and dictionaries are the way to go to do this. It can all be calculated tabularly, grouping poles in sets of two after stripping "A"s from your pole numbers. Then you can create a new point feature class and use an insert cursor and a little basic math to add midpoints.

Something like this:

#poles
pointFc = r"C:\Path\to\Point"
#pole field
poleFld = "POLE_NUMBE"
#line field
lineFld = "LINE_NUMBE"
#output feature class (will be created)
outPointFc = r"C:\path\to\outPoints"

#-----

#import
import arcpy
import os
import re

#create empty dictionary for pole xys by pole number
print "creating dictionary"
di = {}
#iterate table
with arcpy.da.SearchCursor (pointFc, [lineFld, poleFld, "SHAPE@XY"]) as curs:
for line, pole, xy in curs:
#remove letters from pole
pole = pole.replace ("A", "")
pole = pole.replace ("B", "")
pole = pole.replace ("C", "")
#...continue for letters as needed
#alternative: use regular expressions to remove any non-numeric
#at end of string -
#pole = re.sub (r"\D\$", "", pole)

#try to add pole xy to dictionary
try: di [(line, pole)] += [xy]
#pole not yet in dictionary - create new key
except KeyError: di [(line, pole)] = [xy]

#create new feature class
print "creating new feature class"
outPath, outName = os.path.split (outPointFc)
arcpy.CreateFeatureclass_management (outPath, outName, "POINT",
spatial_reference = pointFc)

#add fields for pole and line
for field in [poleFld, lineFld]:

#create insert cursor for output points
print "updating new feature class"
with arcpy.da.InsertCursor (outPointFc, ["SHAPE@XY", poleFld, lineFld]) as curs:
#iterate dictionary and add points to new feature class
for key in sorted (di.keys ()):
#get points
points = di [key]
#get line number and pole number
line, pole = key
#get all Xs
xs = [x for x, y in points]
#get all Ys
ys = [y for x, y in points]
#get midpoint
x = sum (xs) / len (xs)
y = sum (ys) / len (ys)
#create row
row = ((x, y), pole, line)
#insert row
curs.insertRow (row)


Input:

Results:

• Excellent code! +1 from me.. I especially like the pythonic crash and burn approach to new keys. – Michael Stimson Oct 12 '17 at 22:38
• Thank you so much, I will run this tomorrow as soon as I get back to the office. We do have a few instances where there are 3 or 4 poles. Will this handle that? If not there's only a few and I can just brute force it. – Pete Oct 12 '17 at 22:45
• @Pete Then you'd want to add all three (or four) X's and Y's and divide by three (or four), instead of adding and dividing by two. I bet you and your python guy would be able to figure that one out. The simplest way would be to create new if statements prior to inserting the row: if len (points) == 3: #do stuff, if len (points) == 4: #do other stuff. You'll also have to remove Bs and Cs from your pole number in your first cursor I assume. – Emil Brundage Oct 12 '17 at 22:48
• @Pete I couldn't resist - updated the code so it doesn't matter how many poles you have. Just make sure you have enough pole = pole.replace ("X", "") to cover all letters at the end of pole numbers. – Emil Brundage Oct 12 '17 at 23:01
• Thank you so much for this, there are some wonky areas that need to have line manually adjusted, but that's because of our numbering scheming. You've reduced 13,000 points down to 20. Kudos – Pete Oct 13 '17 at 14:11