I have two shapefiles, Point and Polyline. I'm trying to populate my polyline layer with the point data ID (systemID) where the lines start to where they end; eg. create two columns (from and to). The lines already have flow direction assigned to them. I am using Arc Hydro tools with ArcMap.

In Arc Hydro I have tried using Attribute Tools > Generate From/To Nodes for lines but it creates values for To and From that are not from my point data. My other alternative was using Feature to Vertices and joining the result to my point data table to get the systemID. But then I would have to update the polyline layer as well.

Not all the lines have points at the vertices, blank values are fine for them.


3 Answers 3


Sometimes it is better not to use out of the box solution. This is why I suggest:

  1. Populate X and Y fields in node layer, convert them to integers, say cm. Create string field and populate it by concatenating string representations of integers.
  2. Do similar thing in links table for first point in the shape.
  3. Join nodes table to links using latest created fields and transfer nodeID into FROM field.

Go to 2, but use last point to assign TO nodeID.

  • Like it! Field calculate something like str(round(!Shape!.firstPoint.X,3)) + "," + str(round(!Shape!.firstPoint.Y,3)) (Python parser) for from node, str(round(!Shape!.lastPoint.X,3)) + "," + str(round(!Shape!.lastPoint.Y,3)) for to node, then similar to the point (as text) then join... Excellent way of spatial joining without the pain Felix! Round to more significant digits for geographic data (like 7 or 8). Commented Dec 10, 2014 at 4:48
  • Extemely fast as well in a scripting environment, where of course dictionary replaces slow table join
    – FelixIP
    Commented Dec 10, 2014 at 5:06
  • Int (X*100) if they are projected in metres.Alternaively use Michael's code
    – FelixIP
    Commented Dec 10, 2014 at 5:33
  • Definitely simpler than the method in the question I linked to above. Fewer steps, fewer tools, and no license level restrictions. Interesting idea to turn a spatial join into a regular attribute join.
    – Chris W
    Commented Dec 10, 2014 at 18:21

I did the same thing a few months back. I used ArcPy but the script is far too long to post here so I will give you an outline of what I did.

  1. I used a spatial join to detect which points/nodes were within a particular line feature.

  2. Because the spatial join does not take flow direction into account I used ArcPy to ascertain which was the start point and which was the end point. I was able to do this by using the ArcPy describe function to extract coordinate information for the start/end vertices of the line feature and compared these to the coordinate values of the connected points.

  3. Finally, once I worked out which of the to/from points were which I used the setValue function to populate the to/from fields in the original polyline dataset.

Obviously, there is a little more to it than this but I have outlined the main points.

  • In my geometric network, the flow direction could be in any of the cardinal directions. I couldn't possibly ascertain a flow direction from the comparison of raw coordinates unless I was also using a flow direction raster or something equivalent. Even that can be problematic because sometimes pipes are sloped against the natural contours or water is pumped uphill. How can you be certain that your flow direction is correct?
    – Priscilla
    Commented Mar 1, 2019 at 20:08

I was inspired by @FelixIP, but I wanted to write a solution without joins or the creation of extra files, since my network is quite large with 400K+ pipes and 500K+ nodes.

The geometric network build forces the X,Y of the nodes and the pipe ends to be coincident. You can access these locations with the shape tokens in arcpy cursors and match them. The shape tokens for lines return an array of the vertices in the order that they were drawn. In my network, the draw order of the pipes is heavily QA'd because we use this to set the flow directions. So, the first vertex is the start of the pipe, and the last vertex is the end of the pipe.

Reference: ASSETID = id of pipe, UNITID = node id at start of pipe, UNITID2 = node id at end of pipe.

nodes = "mergeNodes"
pipes = "SEWER_1"

nodeDict = {}
pipeDict = {}

#populate node dictionary with X,Y as the key and node ID as the value
for node in arcpy.da.SearchCursor(nodes, ["UNITID", "SHAPE@XY"]):
    nodeDict[(node[1][0], node[1][1])] = node[0]

#populate pipe dictionary with pipe ID as the key and list of X,Y as values 
#vertices populated in the order that the line was draw
#so that [0] is the first vertex and [-1] is the final vertex
for pipe in arcpy.da.SearchCursor(pipes, ["ASSETID", "SHAPE@"]):
    for arrayOb in pipe[1]:
        for point in arrayOb:
            if pipe[0] in pipeDict:
                pipeDict[pipe[0]].append((point.X, point.Y))
                pipeDict[pipe[0]] = [(point.X, point.Y)]

#populate UNITID with the first vertex of the line
#populate UNITID2 with the final vertex of the line
with arcpy.da.UpdateCursor(pipes, ["ASSETID", "UNITID", "UNITID2"]) as cur:
    for pipe in cur:
        if pipeDict[pipe[0]][0] in nodeDict:
            pipe[1] = nodeDict[pipeDict[pipe[0]][0]]
        if pipeDict[pipe[0]][-1] in nodeDict:
            pipe[2] = nodeDict[pipeDict[pipe[0]][-1]]
  • This is 90% of what I do, but I don't go through pipes twice, because nodes dictionary already available.
    – FelixIP
    Commented Apr 27, 2019 at 6:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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