import copy from typing import Optional from typing import Tuple import math def point_to_line_distance(px, py, x1, y1, x2, y2): """ 计算一个点,到另外两个点形成的直线上的垂直距离。(点到直线的距离公式) 返回距离值 """ # 分子部分 numerator = abs((y2 - y1) * px - (x2 - x1) * py + x2 * y1 - y2 * x1) # 分母部分 denominator = math.hypot(y2 - y1, x2 - x1) # 距离 return numerator / denominator def from_main_get_other_point_distance(main_ymj, other_ymj): """ 以main_ymj(主要预埋件)的四个边的中点。 高度中点连线为x轴 宽度中点连线为y轴 计算other_ymj(其他预埋件)的中心点到主要预埋件x轴直线和y轴直线的的垂直距离 返回x垂直距离,y垂直距离 """ # 其他预埋件的中心点坐标 px = other_ymj['x_center'] py = other_ymj['y_center'] # 计算其他预埋件中心点坐标到预埋件左右边中点坐标连线形成的直线的垂直距离 bottom_line_center_x = main_ymj['bottom_line_center_x'] bottom_line_center_y = main_ymj['bottom_line_center_y'] top_line_center_x = main_ymj['top_line_center_x'] top_line_center_y = main_ymj['top_line_center_y'] x_distance = point_to_line_distance(px, py, bottom_line_center_x, bottom_line_center_y, top_line_center_x, top_line_center_y) # 计算其他预埋件中心点坐标到预埋件左右边中点坐标连线形成的直线的垂直距离 left_line_center_x = main_ymj['left_line_center_x'] left_line_center_y = main_ymj['left_line_center_y'] right_line_center_x = main_ymj['right_line_center_x'] right_line_center_y = main_ymj['right_line_center_y'] y_distance = point_to_line_distance(px, py, left_line_center_x, left_line_center_y, right_line_center_x, right_line_center_y) return round(x_distance, 2), round(y_distance, 2) def from_main_get_other_point_distance_bim(main_ymj, other_ymj): """ 与from_main_get_other_point_distance函数类似 只不过计算的是bim图中,其他预埋件相对于主要预埋件的x和y的距离 因为bim是标准的平面图,所以可以直接使用主要预埋件的中心点作为坐标原点,直接计算其他预埋件相对于这个坐标原点的x,y, 返回x垂直距离,y垂直距离 """ main_x = main_ymj["x"] main_x = float(main_x) main_y = main_ymj["center"] main_y = float(main_y) * 1000 other_x = other_ymj["x"] other_x = float(other_x) other_y = other_ymj["center"] other_y = float(other_y) * 1000 x_distance = abs(other_x - main_x) y_distance = abs(other_y - main_y) return round(x_distance, 2), round(y_distance, 2) def cal_deviation(main_ymj, other_ymj_list): """ 计算偏差表 bim和图片中其他预埋件中心点到主要预埋件的偏差值 中心x轴直线偏差值 中心y轴直线的的垂直距离偏差值 欧几里德距离偏差值 @:return [ { 'ymj_code': 预埋件code 'x_deviation': x偏差 'y_deviation': y偏差 'euclidean_deviation': 欧几里德距离的偏差 'average_deviation' : 平均误差 'x_deviation_s': x偏差值,带正负符号 'y_deviation_s': y偏差值,带正负符号 } ] """ # 1.计算图片和bim中,其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离 distance_data = [] # bim 和 图片中其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离,欧几里德距离 for other_ymj in other_ymj_list: # 计算图片中其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离 x_distance_pic, y_distance_pic = from_main_get_other_point_distance(main_ymj, other_ymj) pic_data = { 'x_distance': x_distance_pic, 'y_distance': y_distance_pic, # 欧几里得距离值 'euclidean_distance': math.sqrt(x_distance_pic ** 2 + y_distance_pic ** 2), } # 计算bim中其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离 x_distance_bim, y_distance_bim = from_main_get_other_point_distance_bim(main_ymj, other_ymj) bim_data = { 'x_distance': x_distance_bim, 'y_distance': y_distance_bim, # 欧几里得距离值 'euclidean_distance': math.sqrt(x_distance_bim ** 2 + y_distance_bim ** 2), } distance_data.append( { 'code': other_ymj['code'], 'pic_data': pic_data, # 图片中其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离, 欧几里得距离值 'bim_data': bim_data, # bim中其他预埋件中心点到主要预埋件中心x轴直线和中心y轴直线的的垂直距离, 欧几里得距离值 } ) # 2.对比bim和图片中数据的偏差值 deviation_data = [] # 偏差值列表 for distance in distance_data: pic_data = distance['pic_data'] bim_data = distance['bim_data'] ymj_code = distance['code'] # 计算x偏差 x_deviation = pic_data['x_distance'] - bim_data['x_distance'] # 计算y偏差 y_deviation = pic_data['y_distance'] - bim_data['y_distance'] # 计算欧几里得距离偏差 sqrt_xx_yy_deviation = pic_data['euclidean_distance'] - bim_data['euclidean_distance'] # 计算平均误差 average_deviation = max(abs(x_deviation), abs(y_deviation), abs(sqrt_xx_yy_deviation)) deviation_data.append({ 'ymj_code': ymj_code, 'x_deviation': abs(x_deviation), 'x_deviation_s': x_deviation, 'y_deviation': abs(y_deviation), 'y_deviation_s': y_deviation, 'euclidean_deviation': abs(sqrt_xx_yy_deviation), 'average_deviation': average_deviation, }) # 3.返回计算偏差值列表 return deviation_data def get_point_align_line( px: float, py: float, x_axis: Optional[Tuple[float, float, float, float]], y_axis: Optional[Tuple[float, float, float, float]], x_move_distance: float, y_move_distance: float ) -> Tuple[float, float]: """ 计算一点沿着两点形成的直线方向,移动指定距离之后的坐标值。 注意方向为: 起点 x_axis_x1,x_axis_y1 ==> 终点 x_axis_x2,x_axis_y2 起点 y_axis_x1,y_axis_y1 ==> 终点 y_axis_x2,y_axis_y2 相当于 px, py 沿着 x_axis 和 y_axis 的方向各移动 x_move_distance 和 y_move_distance。 :return: 移动后的新坐标 (new_x, new_y) """ new_x, new_y = px, py # 如果 x 方向有位移 if x_move_distance != 0: x1, y1, x2, y2 = x_axis dx = x2 - x1 dy = y2 - y1 length = math.hypot(dx, dy) if length != 0: x_unit_vec = (dx / length, dy / length) new_x += x_move_distance * x_unit_vec[0] new_y += x_move_distance * x_unit_vec[1] # 如果 y 方向有位移 if y_move_distance != 0: x1, y1, x2, y2 = y_axis dx = x2 - x1 dy = y2 - y1 length = math.hypot(dx, dy) if length != 0: y_unit_vec = (dx / length, dy / length) new_x += y_move_distance * y_unit_vec[0] new_y += y_move_distance * y_unit_vec[1] return new_x, new_y def cal_deviation_table(all_ymj_data): """ 计算每个预埋件的平均偏差表, :param all_ymj_data: :return: [ { 'ymj_code': 主要预埋件 'average_deviation': 平均误差 } ] """ deviation_table = [] # 数值偏差表 for main_ymj in all_ymj_data: other_ymj_list = copy.deepcopy(all_ymj_data) other_ymj_list.remove(main_ymj) # 移除主要预埋件 # 1.计算偏差数据 deviation_data = cal_deviation(main_ymj, other_ymj_list) print(f"main_ymj_code={main_ymj['code']}") print(f"deviation_data={deviation_data}") # 2.计算所有其他预埋件误差的平均值 all_average_deviation = 0 for deviation in deviation_data: # 一个预埋件的误差平均值 average_deviation = deviation['average_deviation'] # 误差 累加起来 all_average_deviation += average_deviation # 除以预埋件数量 all_average_deviation = all_average_deviation / len(deviation_data) deviation_table.append({ 'ymj_code': main_ymj['code'], 'average_deviation': all_average_deviation, }) print(f"📋deviation_table={deviation_table}") return deviation_table def cal_with_manual_measurement(all_ymj_data): """ 根据人工测量出来的偏差值,计算预埋件偏差值 @:return main_ymj_code : 使用了人工测量数据的预埋件 measure_deviation: 其他预埋件相当于人工测量出数据的实际偏差 """ main_ymj_code = None measure_deviation = [] # 深度拷贝一份全部预埋件数据, 防止源数据被修改 all_ymj_data_copy = copy.deepcopy(all_ymj_data) # 遍历查找哪个数据有人工测量的值 is_find = False for idx, ymj in enumerate(all_ymj_data_copy): # 如果有人工测量数据 if 'x_measure_bias' in ymj: is_find = True x_measure_bias = ymj['x_measure_bias'] # x偏差 y_measure_bias = ymj['y_measure_bias'] # y偏差 # 先获取其他预埋件相对于这个预埋件的偏差 other_ymj_list = all_ymj_data_copy other_ymj_list.remove(ymj) deviation_data = cal_deviation(ymj, other_ymj_list) print(f"主预埋件code={ymj['code']}") print(f"其他预埋件偏差={deviation_data}") # 给其他预埋件的偏差值 加上人工测量的偏差 以获得其真实偏差 for deviation in deviation_data: # 带符号的偏差偏差值 x_deviation_s = deviation['x_deviation_s'] y_deviation_s = deviation['y_deviation_s'] # 找到这个预埋件的源数据 for ymj_data in other_ymj_list: # code相同判断 if ymj_data['code'] == deviation['ymj_code']: # 偏差值再加上人工测量的偏差 以获得其真实偏差 measure_deviation.append({ 'ymj_code': deviation['ymj_code'], 'x_deviation': x_deviation_s + x_measure_bias, 'y_deviation': y_deviation_s + y_measure_bias, }) break if is_find: # 只拿取第一个人工测量的预埋件的数据 main_ymj_code = ymj['code'] break if not is_find: print("❗️❗️没有任何一个预埋件存在人工测量的数据") return main_ymj_code,measure_deviation