This is a follow up question based off of this question.

I have a line feature class representing storm drains and a point feature class representing where storm drains are connected. The point feature class has an elevation for each point allowing me to determine the slope of the storm drain as the difference in elevation from start to end divided by the length of the storm drain. However, most of my points act as starting points for one storm drain and end points for another at the same time. I do know that the start point should always be the point with a higher elevation to facilitate water flow.

How can I calculate the slope for each line segment and designate which points are the start/end points for each line?

Would it be better to calculate new lines based off of my points layer that would include a slope field or is there a way to automate this calculation between the two layers?

I have an ArcMap Advanced license but no 3D or Spatial analyst extensions. I am open to Python and QGIS solutions. I just don;t want to have to enter these values in manually as I know there must be a way to automate it.

enter image description here

  • Alternative/longer/Advanced license only method at gis.stackexchange.com/questions/85082 which is linked from the duplicate. If Felix's answer at the dupe is unclear as far as how to apply to your situation, let me know and I can step you through it.
    – Chris W
    Commented Jul 22, 2015 at 19:50
  • @ChrisW there are few things I didn't mention in my solution. a) Truncates by 2 decimals performs much better than rounding b) shapefiles do better than database feature classes, where xy tolerance can play tricks. Truncate is hard to implement using field calculator though
    – FelixIP
    Commented Jul 22, 2015 at 20:05
  • @FelixIP You can always update/expand/improve your answer. :)
    – Chris W
    Commented Jul 22, 2015 at 20:09
  • That method makes a lot of sense but I don't think it addresses the fact that I have points that are both a start point and an end point. So when I do the spatial join both my startJOIN and my endJOIN layers recieve the same values from the original point field. Am I missing something? I want the greater elevations to end up in the FROM field and the lesser elevations to end up in the TO field.
    – cbunn
    Commented Jul 22, 2015 at 23:28
  • @cbunn those methods listed at gis.stackexchange.com/questions/125090/… should work in your situation. Effectively you're selecting a point which falls on a line's endpoint, then using its elevation. It doesn't matter that you may have already used that point's elevation on another line. This is common in arc/node topology operations, where a point represents both the start-point of one line and the end-point of another line Commented Jul 22, 2015 at 23:33

1 Answer 1


To simply state your problem, you have one set of points with a value but those points/values apply to both ends of a line; you want to add two different point values as separate attributes to one line. There are (at least) two ways to do this.

The first is to extract the start and end nodes of all your lines as points. Since you have an Advanced license, this is easy to do with Feature Vertices to Points. You'd have to extract the start and end nodes to separate point files, since there's no attribute created to distinguish them and you'd have stacked points. This process is what allows you to assign the same point value to two different lines as different attributes - the two files will largely have the same points, but each set defines if that point is the start or end of the line.

Once you've done that you can use a Spatial Join between your points with values and the new start/end points to transfer the values (two joins). Then you can regular joins or Join Field (again, twice) based on line ID (which is an attribute of their extracted points) to transfer the start/end values to two new fields in the line data. This is the method outlined at How to assign node IDs to links in a network?

The second method, Felix's solution from Assign point IDs to respective start and end attributes of a polyline, potentially has a lot more field calculation and manipulation, but fewer joins and doesn't have the Advanced license requirement to extract the nodes. Instead it relies on making a unique ID field for the points based on coordinates, and matching that to new fields in the line file that identify the coordinates of the start and end points.

To start with, you need an identifier for the value points. Add two new fields to that point file, X and Y (floats to hold decimals if needed). Add a third called XY but make it a string. Using Calculate Geometry in the attribute table (right-click field heading) you can add the x and y coordinate values to those two new fields, then use a field calculation on the third to combine (concatenate) the values into a single string - something like 734.54,234.87. In theory that should uniquely identify each point.

Now you'll do the same in your line file, except you have to do all those steps twice - once (three fields) for the start points, and once (three more fields) for the end points. There is a special syntax you can use to access the geometry of the line in the field calculator. From Michael's comment on Felix's answer at the other question, an example would be:

str(round(!Shape!.firstPoint.X,3)) + "," + str(round(!Shape!.firstPoint.Y,3))

The syntax I'm referring to is the !shape!.firstPoint.X part. It's discussed in the help files for the Calculate Field tool. You'll also note that Michael's formula is set up to do it all in one calculation, so you'd really only need two fields instead of six because it combines steps. The same could apply to the points if you don't use the Calculate Geometry tool. The rest of the syntax is converting numeric to strings and rounding. Note Felix's comments on your question about truncating and rounding and source xy tolerance. The issue being raised is making sure that the generated ID string actually matches when you do it for both the points and lines since it's based on math to coordinate values.

What you should end up with are three string attribute fields that have matching values across the two feature classes - ID in the points, and a startID and endID in the lines. You'll add two more fields to lines - startelev and endelev At this point you can do two regular joins (or use the Join Field tool as mentioned above) based on the ID fields. One will transfer your point values to the start fields, and the other will do the end fields.

With that complete you can add your new slope field and run the calculation based on the startelev and endelev fields.

  • This is what I ended up doing. But upon further inspection I realized I had storm drain boxes that were actually no resting directly on the line they correspond to in the real world. So I had to create points from the vertices and then to a spatial join with the storm drain boxes to acquire their attributes. Then I followed felix's method and it worked wonderfully! Thank you!
    – cbunn
    Commented Jul 23, 2015 at 19:41

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