I need to calculate slope, or stream gradient, two different ways:

  1. Main channel slope from the pour point to the head/source point along the vector stream network

  2. a local slope consisting of 100 meters up from the pour point

I am using NHD High Resolution data (need to use this dataset as some other calculations are dependent on it so I don't want to go the direction of deriving my own streams from DEM data) and have just shy of 500 "pour points" that are snapped to the NHD High Resolution data. I fully understand the intent and concepts but am struggling with how to compose the methods to automate this for all the sites within the confines of ArcGIS (version 9.3.1). I do have 10m DEM data for the whole study area for getting the elevations.

Obstacles I'm wrestling with mentally:

  • How to deal with identifying all the stream segments that make up the "main channel" so that I can sum their lengths. In some cases near the head of the stream there may be a first-order tributary, but then the main channel also has the same stream order (I've calculated both Strahler and Shreve stream orders using a tool called RivEx so the stream segments are attributed with these as well as their from-nodes and to-nodes via the RivEx tool, and I have the from-nodes and to-nodes). There doesn't seem to be a common attribute in the NHD flowline attributes (e.g., GNIS ID) to essentially dissolve common, or at least to somehow select all of them. Many of the segments simply don't have GNIS ID's.

  • How to measure just 100 m up from a pour point and then to generate a new "from node" to represent that 100 m upstream location to obtain Elevation data and thus calculate the local stream slope.

I've been working some long hours so my brain is probably making this harder than it is...

and I have come across these two other posts on GIS StackExchange but have been struggling with how to adapt them to my needs:

Workflow for determining stream gradient?

Assigning points to their respective Reaches using Stream Gradient?

It seems like all the pieces are there ("m-aware vector streams"), etc. but I'm starting to wonder if that's not the case without, again, a common identifier to identify which upstream segments should be considered part of the downstream "main channel".

Here is an example: Headwater Stream Example

With the shown example, the darker red dots are the nodes, and the bright red dots are the stream sampling points. To calculate a main channel slope, if I write an automation program I need some sort of attribute to cue off of to be able to identify which of the shown upstream segments are part of the "main channel". This case is an instance where the segment that has the sampling points (bright red) and the upstream segement that branches to the left have the same GNIS ID so I could use that. But the NHD data have many instances where smaller streams of this nature simply don't have GNIS ID's at all. And the from/to node info or even the NHD Flow table which I have simply tells you that both upstream segments flow into the sampling point segment. With this example as a demonstration of other instances that lack a GNIS ID, I need a way to automate the decision of WHICH upstream segment to use for calculating the main channel slope in a scripted program. What else could be used to do this?

Another example of where the GNIS ID could not be used as a way to hydrologically identify all of the segments that make up the main channel is where an upstream section has one GNIS ID and unique GNIS Name (e.g., "stream X", and then it takes on a different name (e.g., stream "y") from a confluence with another trib. A point on stream "y" hydrologically still receives main channel flow from stream x but the GNIS ID can't be used to connect the two.

  • Which way is "up"? (100 meters in planimetric view, or some point that is 100 meters higher in elevation) ? Do you also have the flow table? Jun 22, 2011 at 15:19
  • "up" or north if you're confused by the orientation of the screenshot is as you would assume it to be in the image. Just got the Flow tables extracted this morning and in looking at them some with this example, wondering if NHD attributes what is considered the "true" start of the downstream network (which could be used to answer my example question) with a "NetworkStart" value in the Direction attribute field?
    – MapBlast
    Jun 22, 2011 at 15:59
  • but that may not work either (got timed out in the above comment) b/c the record in the Flow table that has the "NetworkStart" value in the Direction field does not have a FromComID value (and thus no way to identify the segment branching to the left as shown)
    – MapBlast
    Jun 22, 2011 at 16:06
  • I'll need to dig through my notes, but as I recall the "DeltaLevel" is used to determine where the main channel is. This is especially important when tracing through a braided river. Do you have an area with braided rivers? If so, set up some relates and see what the logic is for the deltalevel. Jun 22, 2011 at 16:45
  • @Kirk Kuykendall - misread your question about 100 meters...I meant 100 meters upstream from the pour point (using the screenshot image, 100m upstream from each of the red dots respectively).
    – MapBlast
    Jun 22, 2011 at 16:45

1 Answer 1


The slope is obtained by extracting the elevations at the sample point and the point 100 meters upgradient along the main stream (and then dividing their difference by 100). Consider these criteria for finding the upstream point:

  1. It must lie on a stream.

  2. It must be upstream of the sample point.

  3. The stream distance to the sample point must be 100 meters.

  4. Among all such points, it should have the greatest flow.

The latter identifies the main branch of the stream.

To implement these criteria, begin with a stream layer (1). Precompute a flow accumulation grid from a DEM. Given a sample point,

  • find all stream points 100 meters upstream of the sample point (2, 3). (This is properly a graph-theoretic or "network analysis" procedure, suggesting the stream layer should be in a topologically correct oriented vector format.)

  • Extract the flow accumulation values at the remaining points. Select any point having a maximum flow accumulation (4). Use its elevation (already obtained) for the slope calculation.

This workflow moves as quickly as possible to processing a small finite set of points, which consists of fast calculations. The key is the first step. It likely can be done with Network Analyst or by exploiting properties of measured polylines. It can even be done from scratch with little trouble because the topological information is already there. The algorithm requires just a few basic capabilities, in addition to basic list handling functions (add an element to a list and append one list to another):

  • Find the distance between two points known to lie on an arc (an unbranching polyline): Distance(p1, p2, a) (p1, p2:points; a:arc).

  • Given a point on an arc and a distance, find any points on that arc at that distance: Along(p, a, d) (p:point, a:arc; d:distance).

  • Fetch the set of arcs having a common "to node." If v is a node, let Arcs(v) be the set of arcs having v as their "to nodes." This can be found with a database query or, better, by initially placing all arcs into an associative array ("dictionary") indexed by their to nodes.

Every arc a has a unique "from node" (upgradient endpoint), a.From. Every point p has an elevation, p.Elevation, computed if necessary from the DEM.

The input is the sample point p, the arc a on which it lies, and the desired upstream distance d (100 meters). It returns a set of points guaranteed to lie upstream of point p along a at distance d. (There may be no such points, in which case an empty set is returned.) Here is pseudocode (to which some key guard statements, "asserts," are added to establish correctness):

FindUpstream(p:point, a:arc, d:number) {
    Assert(p lies on a)
    Assert(d >= 0)
    output = {} // Empty set
    e = Distance(p, a.From, a)       // Distance from p to the upgradient endpoint of a
    If (e < d) {                     // We have to move up the branches
        For each b in Arcs(a.From) { // Recursively find the upstream points
            Append FindUpstream(a.From, b, d-e) to output
    } Else {                         // Find the upgradient point on a
        For each q in Along(p, a, d) {
            If (q.Elevation >= p.Elevation) {
                Adjoin q to output
        Assert(output contains exactly one point)
    Return output

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.