# Correcting monotonicity errors

There is not a lot of information on this site or any site on how to correct monotonic errors for line vectors in a GIS context. It seems like such a simple problem with a simple fix. Monotonicity is required when a z-enabled feature is required to be ever increasing or decreasing i.e. the z values at each vertex should be consistently going down or up. This is most often seen in hydrology networks than need to be monotonic for network and flow. A monotonic error is flagged when there is a vertex that goes opposite of this trend. See the before and after examples from ESRI below.

Correcting monotonicity errors page from ESRI

Data Reviewer as well as other topology checks can find them. Defense Mapping for ArcGIS claims to fix them by interpolating the z-values before and after the errors to be consistent with the downward or upward slope. After several attempts, The Fix Monotonicity tool in Defense Mapping only makes my lines monotonic by applying one z-value to the entire line essentially flattening it.

Are there any tools or techniques in ArcGIS that will automatically correct these errors?

• I can look at adding the tags when I move from mobile to laptop tomorrow morning. – PolyGeo Sep 7 '17 at 14:59
• Very basic field calculator expression that replace vertex z by one from upstream in case of conflict. Much more fun with true stream Network. – FelixIP Sep 7 '17 at 19:33

Here is an ArcToolbox ready script for a tool that runs on an entire feature layer, or a selection if you happen to have features selected. The tool assumes the start point has a z lower than the end point for ascending monotonic vertices, or a start point with a higher z than the end point for descending monotonic vertices. Single vertices that do not fit the monotonic trend are given an average z value of the neighbouring vertices. If two or more consecutive non-monotonic vertices are found, the first are "flattened" the the z of the previous vertices. Flat line parts are skipped all together.

This is not pretty (thanks to arcpy geometry objects), and there's probably a better solution out there, but it works fairly well.

import arcpy
inFeat = arcpy.GetParameterAsText(0)   #Feature Layer
with arcpy.da.UpdateCursor(inFeat, ["OID@", "SHAPE@"]) as cursor:
for row in cursor:
geom = row[1]
partNum = 0
isIncreasing = None
newGeom = arcpy.Array()
for part in geom:
newPart = arcpy.Array()
# detemine increasing/decreasing
if part[0].Z < part[len(part) - 1].Z:
isIncreasing = True
elif part[0].Z > part[len(part) - 1].Z:
isIncreasing = False
else:
#flat line
arcpy.AddMessage("Line {0} part {1} is flat. Skipping...".format(row[0],partNum))
partNum += 1
continue
zList = []
for i in xrange(len(part)):
pnt = part[i]
zList.append(pnt.Z)
for i in xrange(len(part)):
pnt = part[i]
z = zList[i]
if (i != 0) and (i != len(part) - 1):
zN = zList[i+1]
zP = zList[i-1]
if isIncreasing:
if z < zP:
if zN > zP:
# interpolate Z (i.e. average)
zList[i] = (zP + zN) / 2
arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
else:
# Next Z is greater than Prev Z
# set Z equal to previous point
zList[i] = zP
arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
else:
if z > zP:
if zN < zP:
zList[i] = (zP + zN) / 2
arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
else:
zList[i] = zP
arcpy.AddMessage("Modified line {0}, part {1}, vertex {2}...".format(row[0], partNum, i))
newPnt = arcpy.Point(part[i].X, part[i].Y, zList[i])
newShape = arcpy.Polyline(newGeom, None, True)
row[1] = newShape
cursor.updateRow(row)


I'm unaware of any out of the box tool or third party tools. Not saying there are none, I just don't do much in Z aware geometry. But I have done work using M aware geometry and used various objects in ArcObjects. I think this requires a custom ArcObjects solution that manipulates Z values at the vertex level. If you look at the IZ Interface in the API help, this is a starting point on accessing various functions that can manipulate Z values.

If Arcobjects is too much for you, but python is OK then an approach is to explode the polyline into a sequence of XYZ and then write some fancy code that can fixes the error as it reconstructs the polyline Z geometry.

I think you should edit your question to indicate the range of errors, in your example it is happening at the end of the polyline, is that always the case or can it happen at the start and/or middle?

I selected the answer submitted by @Barbarossa because it does exactly what I was asking in the question. Thanks @Barbarossa !

I did want to submit my own answer in addition to his as an alternative. It also contributes to the body of knowledge on the subject and is an option for the users that have a LP360 license or want to purchase one.

The solution I found is only partially true to the question and is not free. GeoCue, a small software vendor located in Huntsville, Alabama has a solution included in their LP360 software. LP360 is available as both a standalone Windows application and as an extension to ArcGIS. The Feature Analyst tool in LP360 includes a function they are calling Monotonic Hydro Drainage (Downhill/Uphill). This lets you test features for flow downhill and uphill and lets you see them in a table of vertices. It also provides the ability to interpolate the non-monotonic vertex to correct the error and ensure monotonicity.

• Always remember @Barbarossa rocks! :) – Hornbydd Sep 8 '17 at 0:00