【ArcMap】按属性表复制字段并上移一段距离

将属性表中 LXMC 为 名称2 的字段复制并上移20米,在py编辑器中插入以下代码:
# -*- coding: utf-8 -*- import arcpy import os import sys # 重新加载sys并设置默认编码 reload(sys) sys.setdefaultencoding('utf-8') # 设置环境 arcpy.env.overwriteOutput = True try: # 获取当前地图文档 mxd = arcpy.mapping.MapDocument("CURRENT") df = arcpy.mapping.ListDataFrames(mxd)[0] arcpy.AddMessage("脚本开始执行...") # 查找线图层 target_layer = None for layer in arcpy.mapping.ListLayers(mxd): if layer.isFeatureLayer: desc = arcpy.Describe(layer) if desc.shapeType == "Polyline": target_layer = layer break if target_layer is None: arcpy.AddError("未找到线图层") sys.exit() arcpy.AddMessage("找到图层: " + target_layer.name) # 检查LXMC字段是否存在 field_names = [field.name for field in arcpy.ListFields(target_layer)] if "LXMC" not in field_names: arcpy.AddError("图层中不存在 LXMC 字段") sys.exit() # 先选中LXMC为"名称2"的要素 where_clause = "LXMC = '名称2'" arcpy.SelectLayerByAttribute_management(target_layer, "NEW_SELECTION", where_clause) # 检查是否有选中的要素 desc = arcpy.Describe(target_layer) if not desc.FIDSet: arcpy.AddError("没有找到LXMC为'名称2'的要素") sys.exit() # 获取选中要素的数量 result = arcpy.GetCount_management(target_layer) selected_count = int(result.getOutput(0)) arcpy.AddMessage("选中的要素数量: " + str(selected_count)) # 获取数据源路径 data_source = desc.catalogPath arcpy.AddMessage("数据源: " + data_source) # 获取空间参考 spatial_ref = desc.spatialReference arcpy.AddMessage("坐标系: " + spatial_ref.name) # 获取选中要素的OID selected_oids = [] with arcpy.da.SearchCursor(target_layer, ["OID@"]) as cursor: for row in cursor: selected_oids.append(row[0]) arcpy.AddMessage("选中的要素OID: " + str(selected_oids)) # 创建查询条件 oid_field = arcpy.Describe(target_layer).OIDFieldName where_clause = "{} IN ({})".format(arcpy.AddFieldDelimiters(data_source, oid_field), ",".join(str(oid) for oid in selected_oids)) # 获取字段列表 field_names = [] for field in arcpy.ListFields(target_layer): if field.type not in ["OID", "Geometry"]: field_names.append(field.name) arcpy.AddMessage("将复制的字段: " + str(field_names)) # 确定移动距离 - 修改为20米 if spatial_ref.type == "Geographic": y_offset = 0.00018 # 大约20米 (1度≈111公里,20米≈0.00018度) else: y_offset = 20.0 # 20米 arcpy.AddMessage("向上移动距离: " + str(y_offset) + (" 度" if spatial_ref.type == "Geographic" else " 米")) # 开始编辑会话 edit = arcpy.da.Editor(os.path.dirname(data_source)) edit.startEditing(False, True) edit.startOperation() try: # 使用插入游标将移动后的要素添加到同一图层 with arcpy.da.InsertCursor(data_source, ["SHAPE@"] + field_names) as insert_cursor: # 使用搜索游标读取选中的要素 with arcpy.da.SearchCursor(data_source, ["SHAPE@"] + field_names, where_clause) as search_cursor: moved_count = 0 for row in search_cursor: shape = row[0] attributes = row[1:] if shape: # 创建一个新的数组来存储移动后的点 new_parts = arcpy.Array() # 遍历原始几何的每个部分 for part in shape: new_part = arcpy.Array() # 遍历每个点 for point in part: if point: # 创建新点,Y坐标增加偏移量 new_point = arcpy.Point(point.X, point.Y + y_offset) new_part.add(new_point) new_parts.add(new_part) # 创建新的Polyline几何 moved_shape = arcpy.Polyline(new_parts, spatial_ref) # 插入移动后的要素到同一图层 insert_cursor.insertRow([moved_shape] + list(attributes)) moved_count += 1 arcpy.AddMessage("已复制并移动要素 " + str(moved_count)) # 提交编辑 edit.stopOperation() edit.stopEditing(True) arcpy.AddMessage("成功复制并移动 " + str(moved_count) + " 个要素到原始图层") # 刷新视图 arcpy.RefreshActiveView() arcpy.RefreshTOC() # 清除选择集,显示所有要素 target_layer.setSelectionSet("NEW", []) arcpy.AddMessage("操作完成! LXMC为'名称2'的要素已复制并向上移动20米") arcpy.AddMessage("原始要素保持不变,新要素已向上移动20米") arcpy.AddMessage("现在图层中有 " + str(selected_count + moved_count) + " 个要素") arcpy.AddMessage("请使用测量工具验证两条线之间的距离是否为20米") arcpy.AddMessage("注意:由于20米距离很小,可能需要放大查看") except Exception as e: # 中止编辑 edit.stopOperation() edit.stopEditing(False) raise e except Exception as e: arcpy.AddError("错误: " + str(e)) import traceback error_msg = traceback.format_exc() arcpy.AddError(error_msg)
回车后:

进阶版:适用GD
# -*- coding: utf-8 -*- import arcpy import os import sys import math # 重新加载sys并设置默认编码 reload(sys) sys.setdefaultencoding('utf-8') # 设置环境 arcpy.env.overwriteOutput = True def calculate_segment_offset_direction(start_point, end_point, distance=10): """ 计算单个线段的偏移方向 """ dx = end_point.X - start_point.X dy = end_point.Y - start_point.Y # 计算线段长度 length = math.sqrt(dx*dx + dy*dy) if length == 0: return 0, distance # 归一化方向向量 dx /= length dy /= length # 计算垂直向量(左侧偏移) offset_dx = -dy * distance offset_dy = dx * distance return offset_dx, offset_dy def offset_polyline_segments(line_geometry, distance=10, spatial_ref=None): """ 对多段线进行逐段偏移,避免S型线相交 """ new_parts = arcpy.Array() for part in line_geometry: new_part = arcpy.Array() points = [] # 收集所有点 for point in part: if point: points.append((point.X, point.Y)) if len(points) < 2: continue # 对每个线段计算偏移方向 offset_points = [] # 处理第一个点 first_offset_dx, first_offset_dy = calculate_segment_offset_direction( arcpy.Point(points[0][0], points[0][1]), arcpy.Point(points[1][0], points[1][1]), distance ) offset_points.append(( points[0][0] + first_offset_dx, points[0][1] + first_offset_dy )) # 处理中间点 - 使用前后线段方向的平均值 for i in range(1, len(points)-1): # 前一线段的方向 prev_dx, prev_dy = calculate_segment_offset_direction( arcpy.Point(points[i-1][0], points[i-1][1]), arcpy.Point(points[i][0], points[i][1]), distance ) # 后一线段的方向 next_dx, next_dy = calculate_segment_offset_direction( arcpy.Point(points[i][0], points[i][1]), arcpy.Point(points[i+1][0], points[i+1][1]), distance ) # 使用平均值来平滑过渡 avg_dx = (prev_dx + next_dx) / 2 avg_dy = (prev_dy + next_dy) / 2 offset_points.append(( points[i][0] + avg_dx, points[i][1] + avg_dy )) # 处理最后一个点 last_offset_dx, last_offset_dy = calculate_segment_offset_direction( arcpy.Point(points[-2][0], points[-2][1]), arcpy.Point(points[-1][0], points[-1][1]), distance ) offset_points.append(( points[-1][0] + last_offset_dx, points[-1][1] + last_offset_dy )) # 创建新的线段 for x, y in offset_points: new_part.add(arcpy.Point(x, y)) new_parts.add(new_part) return arcpy.Polyline(new_parts, spatial_ref) def check_and_adjust_offset(line_geometry, offset_line, original_endpoints, distance=10, spatial_ref=None): """ 检查偏移线是否与原始线相交,如果相交则调整 """ try: # 检查是否相交 if line_geometry.crosses(offset_line) or line_geometry.overlaps(offset_line): arcpy.AddMessage("检测到偏移线与原始线相交,尝试反向偏移") # 尝试反向偏移 return offset_polyline_segments(line_geometry, -distance, spatial_ref) else: return offset_line except: # 如果几何检查失败,使用距离检查 try: if line_geometry.distanceTo(offset_line) < distance * 0.5: arcpy.AddMessage("偏移距离过小,尝试反向偏移") return offset_polyline_segments(line_geometry, -distance, spatial_ref) else: return offset_line except: return offset_line def smooth_polyline(polyline, tolerance=0.1, spatial_ref=None): """ 对多段线进行平滑处理,减少尖锐转角 """ new_parts = arcpy.Array() for part in polyline: if part.count < 3: new_parts.add(part) continue new_part = arcpy.Array() points = [] # 收集所有点 for point in part: if point: points.append((point.X, point.Y)) # 添加第一个点 new_part.add(arcpy.Point(points[0][0], points[0][1])) # 对中间点进行平滑 for i in range(1, len(points)-1): prev_x, prev_y = points[i-1] curr_x, curr_y = points[i] next_x, next_y = points[i+1] # 计算前后向量的角度 vec1_angle = math.atan2(curr_y - prev_y, curr_x - prev_x) vec2_angle = math.atan2(next_y - curr_y, next_x - curr_x) # 如果角度变化太大,进行平滑 angle_diff = abs(vec1_angle - vec2_angle) if angle_diff > tolerance: # 使用中点 smooth_x = (prev_x + next_x) / 2 smooth_y = (prev_y + next_y) / 2 new_part.add(arcpy.Point(smooth_x, smooth_y)) else: new_part.add(arcpy.Point(curr_x, curr_y)) # 添加最后一个点 new_part.add(arcpy.Point(points[-1][0], points[-1][1])) new_parts.add(new_part) return arcpy.Polyline(new_parts, spatial_ref) def connect_endpoints_to_original(offset_line, original_endpoints, search_distance=30): """ 将偏移线的端点连接到原始线的对应端点 """ new_parts = arcpy.Array() for part in offset_line: new_part = arcpy.Array() points = [] # 收集所有点 for point in part: if point: points.append((point.X, point.Y)) if len(points) < 2: new_parts.add(part) continue # 处理起点 - 连接到最近的原始起点 start_x, start_y = points[0] closest_start = find_closest_original_endpoint(start_x, start_y, original_endpoints, search_distance) if closest_start: points[0] = closest_start # 处理终点 - 连接到最近的原始终点 end_x, end_y = points[-1] closest_end = find_closest_original_endpoint(end_x, end_y, original_endpoints, search_distance) if closest_end: points[-1] = closest_end # 重新构建线段 for x, y in points: new_part.add(arcpy.Point(x, y)) new_parts.add(new_part) return arcpy.Polyline(new_parts, offset_line.spatialReference) def find_closest_original_endpoint(point_x, point_y, original_endpoints, max_distance=30): """ 找到距离最近的原始端点 """ min_distance = float('inf') closest_point = None for orig_oid, endpoints in original_endpoints.items(): for endpoint_type, coords in endpoints.items(): orig_x, orig_y = coords distance = math.sqrt((point_x - orig_x)**2 + (point_y - orig_y)**2) if distance < min_distance and distance < max_distance: min_distance = distance closest_point = (orig_x, orig_y) return closest_point try: # 获取当前地图文档 mxd = arcpy.mapping.MapDocument("CURRENT") df = arcpy.mapping.ListDataFrames(mxd)[0] arcpy.AddMessage("开始处理复杂线形...") # 查找线图层 target_layer = None for layer in arcpy.mapping.ListLayers(mxd): if layer.isFeatureLayer: desc = arcpy.Describe(layer) if desc.shapeType == "Polyline": target_layer = layer break if target_layer is None: arcpy.AddError("未找到线图层") sys.exit() arcpy.AddMessage("找到图层: " + target_layer.name) # 检查字段是否存在 field_names = [field.name for field in arcpy.ListFields(target_layer)] if "JSDJ" not in field_names: arcpy.AddError("图层中不存在 JSDJ 字段") sys.exit() if "XCFX" not in field_names: arcpy.AddError("图层中不存在 XCFX 字段") sys.exit() # 选中JSDJ为"一级公路"且XCFX为"A"的要素(上行线) where_clause = "JSDJ = '一级公路' AND XCFX = 'A'" arcpy.SelectLayerByAttribute_management(target_layer, "NEW_SELECTION", where_clause) # 检查是否有选中的要素 desc = arcpy.Describe(target_layer) if not desc.FIDSet: arcpy.AddError("没有找到JSDJ为'一级公路'且XCFX为'A'的要素") sys.exit() # 获取选中要素的数量 result = arcpy.GetCount_management(target_layer) selected_count = int(result.getOutput(0)) arcpy.AddMessage("选中的上行线要素数量: " + str(selected_count)) # 获取数据源路径 data_source = desc.catalogPath arcpy.AddMessage("数据源: " + data_source) # 获取空间参考 spatial_ref = desc.spatialReference arcpy.AddMessage("坐标系: " + spatial_ref.name) # 获取选中要素的OID selected_oids = [] with arcpy.da.SearchCursor(target_layer, ["OID@"]) as cursor: for row in cursor: selected_oids.append(row[0]) # 创建查询条件 oid_field = arcpy.Describe(target_layer).OIDFieldName where_clause = "{} IN ({})".format(arcpy.AddFieldDelimiters(data_source, oid_field), ",".join(str(oid) for oid in selected_oids)) # 获取字段列表 copy_field_names = [] for field in arcpy.ListFields(target_layer): if field.type not in ["OID", "Geometry"]: copy_field_names.append(field.name) # 确定移动距离 if spatial_ref.type == "Geographic": move_distance = 0.00009 # 大约10米 else: move_distance = 10.0 # 10米 arcpy.AddMessage("偏移距离: " + str(move_distance) + (" 度" if spatial_ref.type == "Geographic" else " 米")) # 开始编辑会话 edit = arcpy.da.Editor(os.path.dirname(data_source)) edit.startEditing(False, True) edit.startOperation() try: # 存储原始要素的起点和终点 original_endpoints = {} with arcpy.da.SearchCursor(data_source, ["OID@", "SHAPE@"], where_clause) as cursor: for row in cursor: oid = row[0] shape = row[1] if shape: first_point = shape.firstPoint last_point = shape.lastPoint original_endpoints[oid] = { 'first': (first_point.X, first_point.Y), 'last': (last_point.X, last_point.Y) } # 使用插入游标将偏移后的要素添加到同一图层 new_features_oids = [] with arcpy.da.InsertCursor(data_source, ["SHAPE@"] + copy_field_names) as insert_cursor: with arcpy.da.SearchCursor(data_source, ["OID@", "SHAPE@"] + copy_field_names, where_clause) as search_cursor: moved_count = 0 for row in search_cursor: oid = row[0] shape = row[1] attributes = row[2:] # 修改XCFX字段的值 modified_attributes = list(attributes) if "XCFX" in copy_field_names: xcfx_index = copy_field_names.index("XCFX") modified_attributes[xcfx_index] = "B" if shape: arcpy.AddMessage("处理要素 {}...".format(oid)) # 方法1: 逐段偏移 offset_shape = offset_polyline_segments(shape, move_distance, spatial_ref) # 检查并调整偏移 offset_shape = check_and_adjust_offset(shape, offset_shape, original_endpoints, move_distance, spatial_ref) # 平滑处理 offset_shape = smooth_polyline(offset_shape, 0.2, spatial_ref) # 连接端点 offset_shape = connect_endpoints_to_original(offset_shape, original_endpoints, move_distance * 3) # 插入偏移后的要素 insert_cursor.insertRow([offset_shape] + modified_attributes) moved_count += 1 arcpy.AddMessage("成功处理要素 {}".format(oid)) # 获取新创建要素的OID new_where_clause = "XCFX = 'B'" with arcpy.da.SearchCursor(data_source, ["OID@"], new_where_clause) as cursor: for row in cursor: new_features_oids.append(row[0]) arcpy.AddMessage("新创建的下行线要素数量: " + str(len(new_features_oids))) # 提交编辑 edit.stopOperation() edit.stopEditing(True) arcpy.AddMessage("成功处理 " + str(moved_count) + " 个要素") # 刷新视图 arcpy.RefreshActiveView() arcpy.RefreshTOC() # 清除选择集 target_layer.setSelectionSet("NEW", []) arcpy.AddMessage("处理完成! 复杂线形已成功复制为下行线") arcpy.AddMessage("采用逐段偏移算法,避免S型线相交问题") except Exception as e: # 中止编辑 edit.stopOperation() edit.stopEditing(False) raise e except Exception as e: arcpy.AddError("错误: " + str(e)) import traceback error_msg = traceback.format_exc() arcpy.AddError(error_msg)
浙公网安备 33010602011771号