|
@@ -0,0 +1,190 @@
|
|
|
+import math
|
|
|
+import json
|
|
|
+
|
|
|
+
|
|
|
+class Point(object):
|
|
|
+ """点类"""
|
|
|
+
|
|
|
+ def __init__(self, x=0, y=0):
|
|
|
+ self.x = x
|
|
|
+ self.y = y
|
|
|
+
|
|
|
+ def distance(self, pt):
|
|
|
+ x_diff = self.x - pt.x
|
|
|
+ y_diff = self.y - pt.y
|
|
|
+ return math.sqrt(x_diff ** 2 + y_diff ** 2)
|
|
|
+
|
|
|
+# 1度的角度误差
|
|
|
+slope_delta = 1 / 180 * math.pi
|
|
|
+
|
|
|
+
|
|
|
+class Segment:
|
|
|
+ """线段类"""
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def create_by_point(cls, x1, y1, x2, y2):
|
|
|
+ # point1 = Point(x1, y1)
|
|
|
+ # point2 = Point(x2, y2)
|
|
|
+ return cls(Point(x1, y1), Point(x2, y2))
|
|
|
+
|
|
|
+ def __init__(self, p1, p2):
|
|
|
+ self.point1 = p1
|
|
|
+ self.point2 = p2
|
|
|
+
|
|
|
+ # 获取线段的斜率
|
|
|
+ def get_slope(self):
|
|
|
+ if self.point1.x == self.point2.x:
|
|
|
+ return None
|
|
|
+ return (self.point2.y - self.point1.y) / (self.point2.x - self.point1.x)
|
|
|
+
|
|
|
+ # 获取点在线段(直线)上的投影点
|
|
|
+ def get_projective_point(self, out_point):
|
|
|
+ slope = self.get_slope()
|
|
|
+ if slope is None:
|
|
|
+ return Point(self.point1.x, out_point.y)
|
|
|
+ if slope == 0:
|
|
|
+ return Point(out_point.x, self.point1.y)
|
|
|
+ tmp_x = ((slope * self.point1.x + out_point.x / slope + out_point.y - self.point1.y) / (1 / slope + slope))
|
|
|
+ tmp_y = (-1 / slope * (tmp_x - out_point.x) + out_point.y)
|
|
|
+ return Point(tmp_x, tmp_y)
|
|
|
+
|
|
|
+ # 是否投影点在线段上
|
|
|
+ def is_projective_point_on_segment(self, point):
|
|
|
+ if (point.x > self.point1.x and point.x > self.point2.x) or (
|
|
|
+ point.x < self.point1.x and point.x < self.point2.x):
|
|
|
+ return False
|
|
|
+ if (point.y > self.point1.y and point.y > self.point2.y) or (
|
|
|
+ point.y < self.point1.y and point.y < self.point2.y):
|
|
|
+ return False
|
|
|
+ return True
|
|
|
+
|
|
|
+ # 是否近似相等
|
|
|
+ def is_similar_parallel(self, other_segment):
|
|
|
+ k1 = self.get_slope()
|
|
|
+ k2 = other_segment.get_slope()
|
|
|
+ # 计算反正切 (与x轴的角度)
|
|
|
+ if k1 is None:
|
|
|
+ angle1 = math.pi / 2
|
|
|
+ else:
|
|
|
+ angle1 = abs(math.atan(k1))
|
|
|
+ if k2 is None:
|
|
|
+ angle2 = math.pi / 2
|
|
|
+ else:
|
|
|
+ angle2 = abs(math.atan(k2))
|
|
|
+ if abs(angle1 - angle2) < slope_delta:
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 在近似相等后, 是否有线段端点的投影点在线段上并且垂线距离小于25cm
|
|
|
+ def is_projective_point_satisfy(self, oupoint):
|
|
|
+ project_point = self.get_projective_point(oupoint)
|
|
|
+ if self.is_projective_point_on_segment(project_point):
|
|
|
+ distance = project_point.distance(oupoint)
|
|
|
+ if distance < 250:
|
|
|
+ return True
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+column_project_id = 'project_id'
|
|
|
+column_location_one = 'location_one'
|
|
|
+column_location_two = 'location_two'
|
|
|
+column_space_id_one = 'space_id_one'
|
|
|
+column_space_id_two = 'space_id_two'
|
|
|
+column_zone_type = 'zone_type'
|
|
|
+
|
|
|
+column_id = 'id'
|
|
|
+column_bim_location = 'bim_location'
|
|
|
+column_floor_id = 'floor_id'
|
|
|
+column_object_type = 'object_type'
|
|
|
+column_outline = 'outline'
|
|
|
+key_x = 'X'
|
|
|
+key_y = 'Y'
|
|
|
+
|
|
|
+
|
|
|
+# 将输入数据按照楼层id, 业务空间类型分类
|
|
|
+def classify(space_list):
|
|
|
+ current_floor_id = ''
|
|
|
+ current_object_type = ''
|
|
|
+ current_sub_arr = []
|
|
|
+ space_arr = []
|
|
|
+ for row in space_list:
|
|
|
+ if row.get(column_floor_id) == current_floor_id and row.get(column_object_type) == current_object_type:
|
|
|
+ current_sub_arr.append(row)
|
|
|
+ else:
|
|
|
+ current_floor_id = row.get(column_floor_id)
|
|
|
+ current_object_type = row.get(column_object_type)
|
|
|
+ current_sub_arr = [row]
|
|
|
+ space_arr.append(current_sub_arr)
|
|
|
+ for sub_arr in space_arr:
|
|
|
+ if len(sub_arr) == 1:
|
|
|
+ space_arr.remove(sub_arr)
|
|
|
+ return space_arr
|
|
|
+
|
|
|
+
|
|
|
+# 根据空间分组, 获取一个分组的外轮廓线段数组
|
|
|
+def get_outline_segment(space_sub_arr):
|
|
|
+ segment_arr = []
|
|
|
+ for single_space in space_sub_arr:
|
|
|
+ single_space_segments = []
|
|
|
+ segment_arr.append(single_space_segments)
|
|
|
+ json_str = single_space.get(column_outline)
|
|
|
+ outline_json = json.loads(str(json_str).replace('\'', '"'))
|
|
|
+ if outline_json is None or len(outline_json) == 0:
|
|
|
+ continue
|
|
|
+ for i in range(0, len(outline_json)):
|
|
|
+ single_area = outline_json[0]
|
|
|
+ if single_area is None or len(single_area) == 0:
|
|
|
+ continue
|
|
|
+ single_area_outline = single_area[0]
|
|
|
+ for j in range(0, len(single_area_outline) - 1):
|
|
|
+ point1_json = single_area_outline[j]
|
|
|
+ point2_json = single_area_outline[j + 1]
|
|
|
+ seg = Segment.create_by_point(point1_json.get(key_x), point1_json.get(key_y), point2_json.get(key_x), point2_json.get(key_y))
|
|
|
+ single_space_segments.append(seg)
|
|
|
+ return segment_arr
|
|
|
+
|
|
|
+
|
|
|
+# 是否一个空间(线段集合)与另外一个空间(线段集合)相邻
|
|
|
+def is_satisfy_adjacent(single_space_segments, other_space_segments):
|
|
|
+ for segment1 in single_space_segments:
|
|
|
+ for segment2 in other_space_segments:
|
|
|
+ # 如果两个线段近似平行, 则获取线段两端在另外一条线段上的投影,
|
|
|
+ # 投影点如果有一个在线段上并且垂线长度小于25cm, 则认为是满足相邻条件
|
|
|
+ if segment1.is_similar_parallel(segment2):
|
|
|
+ if segment1.is_projective_point_satisfy(segment2.point1):
|
|
|
+ return True
|
|
|
+ elif segment1.is_projective_point_satisfy(segment2.point2):
|
|
|
+ return True
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+# 计算一个分组内的邻接关系
|
|
|
+def calc_adjacent_sub_arr(space_sub_arr, space_adjacent):
|
|
|
+ segment_arr = get_outline_segment(space_sub_arr)
|
|
|
+ for i in range(0, len(segment_arr) - 1):
|
|
|
+ single_space_segments = segment_arr[i]
|
|
|
+ for j in range(i + 1, len(segment_arr)):
|
|
|
+ other_space_segments = segment_arr[j]
|
|
|
+ result = is_satisfy_adjacent(single_space_segments, other_space_segments)
|
|
|
+ if result:
|
|
|
+ single_result_row = dict()
|
|
|
+ space_one = space_sub_arr[i]
|
|
|
+ space_two = space_sub_arr[j]
|
|
|
+ single_result_row[column_zone_type] = space_one.get(column_object_type)
|
|
|
+ single_result_row[column_location_one] = space_one.get(column_bim_location)
|
|
|
+ single_result_row[column_location_two] = space_two.get(column_bim_location)
|
|
|
+ single_result_row[column_space_id_one] = space_one.get(column_id)
|
|
|
+ single_result_row[column_space_id_two] = space_two.get(column_id)
|
|
|
+ single_result_row[column_floor_id] = space_one.get(column_floor_id)
|
|
|
+ space_adjacent.append(single_result_row)
|
|
|
+
|
|
|
+
|
|
|
+def calc_space_adjacent(space_list):
|
|
|
+ space_arr = classify(space_list)
|
|
|
+ space_adjacent = []
|
|
|
+ for space_sub_arr in space_arr:
|
|
|
+ calc_adjacent_sub_arr(space_sub_arr, space_adjacent)
|
|
|
+ return space_adjacent
|