9

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 ArcHydro tools but Arcpy is also fine.

In ArcHydro 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. I am sure there is a easier way of doing it.

Note: not all the lines have points at the vertices, blank values are fine for them.

  • So your points have a field 'systemID' that needs to go in FNode, TNode fields in the polylines... is that right? – Michael Stimson Dec 10 '14 at 4:04
  • There's a question like this around here somewhere with an answer that's basically line to point start and end as separate feature classes, intersect the two (individually) with your point data to get the ids, then spatial join them back to the lines using the line ID and assigning to the correct start or end field (might be a field calculate in there). I can't seem to find it right now though. Found it while digging around for this one. – Chris W Dec 10 '14 at 4:25
  • Here it is: gis.stackexchange.com/questions/85082 And it was Feature Vertices to points, not line or features to points. Pretty much what you mention at the end of the question. – Chris W Dec 10 '14 at 4:31
  • @ChrisW, I really like the answer by FelixIP over that one. It's only two attribute joins. – Michael Stimson Dec 10 '14 at 4:52
12

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

  1. Populate X an Y fields in node layer, convert them to integers, say cm. Create string field and populate it by concatenating string represantations 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.

Goto 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). – Michael Stimson Dec 10 '14 at 4:48
  • Extemely fast as well in a scripting environment, where of course dictionary replaces slow table join – FelixIP Dec 10 '14 at 5:06
  • Int (X*100) if they are projected in metres.Alternaively use Michael's code – FelixIP Dec 10 '14 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 Dec 10 '14 at 18:21
1

I done the very 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 Mar 1 at 20:08
1

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))
            else: 
                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]]
        cur.updateRow(pipe)
  • This is 90% of what I do, but I don't go through pipes twice, because nodes dictionary already available. – FelixIP Apr 27 at 6:05

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.