|
# -*- coding: utf-8 -*- |
|
import os |
|
from math import sqrt, hypot |
|
from typing import Tuple |
|
|
|
import arcpy |
|
|
|
|
|
def compare(a: Tuple[int, int], b: Tuple[int, int], distance: float): |
|
return hypot(a[0] - b[0], a[1] - b[1]) < distance |
|
|
|
# Exact comparison |
|
# return a == b |
|
|
|
|
|
def collect(feature_class): |
|
desc = arcpy.Describe(feature_class) |
|
tolerance = 2 * sqrt(2) * desc.spatialReference.xyTolerance |
|
|
|
with arcpy.da.SearchCursor(feature_class, ['OID@', 'SHAPE@']) as cursor: |
|
for i, (oid, shape) in enumerate(cursor): |
|
if not i % 100_000: |
|
arcpy.AddMessage(f'{i:,}') |
|
vertices = [(p.X, p.Y) for p in shape.getPart()[0]] |
|
prev = vertices[0] |
|
for j, curr in enumerate(vertices[1:]): |
|
if compare(prev, curr, tolerance): |
|
yield oid, f"{j}-{j+1}", curr |
|
prev = curr |
|
|
|
|
|
class Toolbox(object): |
|
def __init__(self): |
|
"""Define the toolbox (the name of the toolbox is the name of the |
|
.pyt file).""" |
|
self.label = "Toolbox" |
|
self.alias = "" |
|
|
|
# List of tool classes associated with this toolbox |
|
self.tools = [IdentifyDuplicateVertices] |
|
|
|
|
|
class IdentifyDuplicateVertices(object): |
|
def __init__(self): |
|
"""Define the tool (tool name is the name of the class).""" |
|
self.label = "Identify Duplicate Vertices" |
|
self.description = "" |
|
self.canRunInBackground = False |
|
|
|
def getParameterInfo(self): |
|
"""Define parameter definitions""" |
|
line = arcpy.Parameter(name='input_lines', |
|
displayName='Line Layer', |
|
direction='Input', |
|
datatype='GPFeatureLayer', |
|
parameterType='Required') |
|
point = arcpy.Parameter(name='output_points', |
|
displayName='Output Vertices', |
|
direction='Output', |
|
datatype='DEFeatureClass', |
|
parameterType='Required') |
|
|
|
point.value = 'memory/duplicate_vertices' |
|
|
|
return [line, point] |
|
|
|
def execute(self, parameters, messages): |
|
"""The source code of the tool.""" |
|
line, point = [p.value for p in parameters] |
|
|
|
|
|
path, name = os.path.split(point.value) |
|
dupe_fc = arcpy.CreateFeatureclass_management(out_path=path, |
|
out_name=name, |
|
geometry_type='POINT', |
|
spatial_reference=arcpy.Describe(line).spatialReference)[0] |
|
arcpy.AddField_management(in_table=dupe_fc, field_name='ORIG_FID', field_type='LONG') |
|
arcpy.AddField_management(in_table=dupe_fc, field_name='VERTEX_PAIR', field_type='TEXT', field_length=50) |
|
|
|
with arcpy.da.InsertCursor(dupe_fc, ['ORIG_FID', 'VERTEX_PAIR', 'SHAPE@']) as cursor: |
|
for row in collect(line): |
|
cursor.insertRow(row) |