7

I’m looking for an ArcGIS tool or script that will delete all the collinear vertices in line features. I tried the Simplify Line tool and Generalize in the Advanced Toolbar. The tools sort of work, but they convert curves to segments by adding vertices. I want to keep the original curves intact.

I also tried several Generalize transformers in FME, but get similar results. Any thoughts?

Here is a before and after example of what I'm trying to achieve. This was done by deleting the vertices manually.

Before enter image description here

After enter image description here

2

I've written a short Python script that uses arcpy to update the shapes of the features in a polyline feature class.

Take a backup of your data before running this as it modifies the features in place!

  1. Script works for single part as well as multipart lines.
  2. You can choose what to do with the curves: they are either preserved without removing any redundant vertices this particular feature might have OR densified with the specified distance value. Look up the .densify() method on arcpy.Geometry object. You could alternatively use the Densify GP tool to fix your curves before running this script if you would like to experiment with the deviation values.
  3. Only polyline feature classes are supported (no points/polygons). Could be extended to support polygons though.

The code:

import arcpy


#----------------------------------------------------------------------
def are_collinear(p1, p2, p3, tolerance=0.5):
    """return True if 3 points are collinear.
    tolerance value will decide whether lines are collinear; may need
    to adjust it based on the XY tolerance value used for feature class"""
    x1, y1 = p1[0], p1[1]
    x2, y2 = p2[0], p2[1]
    x3, y3 = p3[0], p3[1]
    res = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)
    if -tolerance <= res <= tolerance:
        return True


#----------------------------------------------------------------------
def get_redundant_vertices(vertices):
    """get redundant vertices from a line shape vertices"""
    indexes_of_vertices_to_remove = []
    start_idx, middle_index, end_index = 0, 1, 2
    for i in range(len(vertices)):
        p1, p2, p3 = vertices[start_idx:end_index + 1]
        if are_collinear(p1, p2, p3):
            indexes_of_vertices_to_remove.append(middle_index)

        start_idx += 1
        middle_index += 1
        end_index += 1
        if end_index == len(vertices):
            break
    return indexes_of_vertices_to_remove


#----------------------------------------------------------------------
def clean_geometries(fc, densify_curves=False):
    """clean polyline features in the fc removing redundant vertices"""
    in_sr = arcpy.Describe(fc).spatialReference.factoryCode

    with arcpy.da.UpdateCursor(fc, ['SHAPE@', 'OID@']) as ucur:
        for row in ucur:
            print "OBJECTID", row[1]
            cleaned_parts = []
            shape = row[0]

            if 'curvePaths' in shape.JSON:
                if densify_curves:
                    shape = shape.densify('DISTANCE', 1, 1)
                else:
                    continue

            for part in range(shape.partCount):
                vertices = [(p.X, p.Y) for p in shape.getPart(part)]
                if len(vertices) < 3:  #polyline's part consists of 2 vertices
                    continue
                vertices_to_remove = get_redundant_vertices(vertices)
                vertices_to_keep = [
                    val for idx, val in enumerate(vertices)
                    if idx not in vertices_to_remove
                ]
                cleaned_part_as_array = arcpy.Array(
                    [arcpy.Point(*coords) for coords in vertices_to_keep])
                cleaned_parts.append(cleaned_part_as_array)

            if cleaned_parts:
                cleaned_shape = arcpy.Polyline(
                    arcpy.Array(cleaned_parts), in_sr)
                row[0] = cleaned_shape
                ucur.updateRow(row)


if __name__ == '__main__':
    fc = r'C:\GIS\Temp\ArcGISHomeFolder\Default.gdb\_SimpleRoads'
    #r'C:\GIS\Temp\ArcGISHomeFolder\empty.gdb\roads'
    clean_geometries(fc, densify_curves=False)
  • Alex T. regarding your 3rd point, how could we extend your solution to simplify polygon vertices knowing that when a shape is cleaned and updated the contiguous shapes to that one should follow that simplification so the output polygon layer would not have gaps? Generalize and Simplify tools (Arcgis) are not a good fit to our scenario. – AbreuFreire May 22 at 10:03
  • @AbreuFreire it has been a while since I worked with arcpy and unfortunately I don't use it any longer and would not be able to help further, so sorry. I think if you could post a new question explaining in detail what you are trying to achieve and link to the code in this answer for the reference, you should get help from other users. Good luck! – Alex Tereshenkov May 22 at 20:26
1

I think that you will need to write a script that works along each line from its start node and takes three vertex coordinates at a time.

If the angle between the first and middle vertex is the same (within a tolerance you set) as between the middle and third vertex then when you re-write the line omit the middle vertex.

If you start writing such code and get stuck, then present it here and our users are often willing to help.

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.