1. 业务空间有所在楼层
2. 业务空间有外轮廓
1. 查出所有有所在楼层关系, 并且外轮廓不是null的业务空间
2. 根据所在楼层, 业务空间分区类型来将业务空间分为不同的组 (例如 : A 楼层下的默认业务空间是一组, A楼层下的空调分区是另外一组)
3. 计算每个分组内的业务空间的相邻关系
计算相邻算法 (如下图):
1. 在一个分组内, 遍历每个业务空间上组成外轮廓的每条线段, 根据线段的长度, 是否超过delta_distance来区分成两种情况判断
1). 如果线段长度大于delta_distance, 假如图中线段AB长度大于delta_distance, 则在距离A点delta_distance的距离找到点P5,
在P5点处作AB线段的垂线线段, 上下两边长度均为door_length长, 找到点P4和P6. 同理可以找到点P7和P9.
2). 如果线段长度小于或等于delta_distance, 假设线段AE长度小于delta_distance, 则在线段中间点P2处作垂直于AE的垂线线段,
上下两边长度均为door_length长, 找到点P1和P3.
2. 将上面找到的点, 依次与组内其他业务空间外轮廓做一次点是否在多边形内的判断, 如果在, 则认为该多边形与图中多边形相邻

[
[
[
{点坐标},
{点坐标}...
], // 子轮廓的外轮廓
[
{点坐标},
{点坐标}...
], // 子轮廓的第n个需要被排除的轮廓
], // 子轮廓
[
[
{点坐标},
{点坐标}...
]
],
]
源码
CREATE OR REPLACE FUNCTION "public"."rel_sp2sp_v2"("project_id" varchar)
RETURNS "pg_catalog"."bool" AS $BODY$
import math
import json
from shapely.geometry import Polygon
from matplotlib.path import Path
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_added_polygon = 'polygon'
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'
delta_distance = 200
door_length = 1500
def get_segment_distance(x1, y1, x2, y2):
x_diff = x1 - x2
y_diff = y1 - y2
return math.sqrt(x_diff ** 2 + y_diff ** 2)
def get_vertical_k(k):
if k is None:
return 0
if k == 0:
return None
return 1 / k
def get_a_by_point_k(x, y, k):
if k is None:
return None
return y - k * x
def get_point_by_distance(base_x, base_y, k, a, distance):
if k is None:
return base_x, base_y + distance, base_x, base_y - distance
vector_x1 = math.sqrt(distance ** 2 / (1 + k ** 2))
vector_x2 = -vector_x1
vector_y1 = k * vector_x1
vector_y2 = k * vector_x2
return base_x + vector_x1, base_y + vector_y1, base_x + vector_x2, base_y + vector_y2
def get_point_by_distance_on_segment(base_x, base_y, k, a, distance, vec_x, vec_y):
x1, y1, x2, y2 = get_point_by_distance(base_x, base_y, k, a, distance)
distance1 = get_segment_distance(x1, y1, vec_x, vec_y)
distance2 = get_segment_distance(x2, y2, vec_x, vec_y)
if distance1 > distance2:
return x2, y2
return x1, y1
def get_points(x1, y1, x2, y2):
if x1 == y1 and x2 == y2:
return None, None, None, None
distance = get_segment_distance(x1, y1, x2, y2)
if x1 == x2:
k = None
else:
k = (y1 - y2) / (x1 - x2)
vertical_k = get_vertical_k(k)
a = get_a_by_point_k(x1, y1, k)
if distance > delta_distance:
seg_x1, seg_y1 = get_point_by_distance_on_segment(x1, y1, k, a, delta_distance, x2, y2)
seg_x2, seg_y2 = get_point_by_distance_on_segment(x2, y2, k, a, delta_distance, x1, y1)
vertical_a1 = get_a_by_point_k(seg_x1, seg_y1, vertical_k)
vertical_a2 = get_a_by_point_k(seg_x2, seg_y2, vertical_k)
dest_x1, dest_y1, dest_x2, dest_y2 = get_point_by_distance(seg_x1, seg_y1, vertical_k, vertical_a1, door_length)
dest_x3, dest_y3, dest_x4, dest_y4 = get_point_by_distance(seg_x2, seg_y2, vertical_k, vertical_a2, door_length)
return [dest_x1, dest_x2, dest_x3, dest_x4], [dest_y1, dest_y2, dest_y3, dest_y4]
else:
seg_x1, seg_y1 = get_point_by_distance_on_segment(x1, y1, k, a, distance/2, x2, y2)
vertical_a1 = get_a_by_point_k(seg_x1, seg_y1, vertical_k)
dest_x1, dest_y1, dest_x2, dest_y2 = get_point_by_distance(seg_x1, seg_y1, vertical_k, vertical_a1, door_length)
return [dest_x1, dest_x2], [dest_y1, dest_y2]
def get_polygon(single_poly):
poly_len = len(single_poly)
poly = []
for i in range(poly_len):
pair = single_poly[i]
poly.append((pair[key_x], pair[key_y]))
return Path(poly)
def get_outer_polygon_arr(raw_outline):
try:
arr = []
outline_json = json.loads(raw_outline)
for i in range(len(outline_json)):
try:
single_polygon = get_polygon(outline_json[i][0])
except Exception as ex:
continue
arr.append(single_polygon)
return arr
except Exception as e:
return []
def is_point_in_polygons(point, polygon_arr):
try:
for polygon in polygon_arr:
try:
if polygon.contains_points([point], None, -0.0001):
return True
except Exception as ee:
plpy.warning("point in polygon : {0}".format(ee))
return False
except Exception as e:
plpy.warning(e)
return False
def check_is_in_rel(space_adjacent, probe_id, id):
if probe_id in space_adjacent:
rel_set = space_adjacent[probe_id]
if id in rel_set:
return True
return False
def check_is_in_rel_bidirection(space_adjacent, probe_id, id):
is_in = check_is_in_rel(space_adjacent, probe_id, id)
is_in = is_in or check_is_in_rel(space_adjacent, id, probe_id)
return is_in
def insert_into_rel(space_adjacent, probe_id, id):
if check_is_in_rel_bidirection(space_adjacent, probe_id, id):
return
if probe_id not in space_adjacent:
space_adjacent[probe_id] = set()
rel_set = space_adjacent[probe_id]
rel_set.add(id)
def calc_adjacent_sub_arr(space_sub_arr, space_adjacent, space_info):
for space_row in space_sub_arr:
raw_outline = space_row.get(column_outline)
id = space_row.get(column_id)
space_info[id] = space_row
outline_json = json.loads(raw_outline)
for i in range(len(outline_json)):
try:
single_poly = outline_json[i][0]
except Exception as ee :
continue
poly_len = len(single_poly)
for idx in range(1, poly_len):
segment_point1 = single_poly[idx - 1]
segment_point2 = single_poly[idx]
x_arr, y_arr = get_points(segment_point1[key_x], segment_point1[key_y], segment_point2[key_x], segment_point2[key_y])
for probe_space_row in space_sub_arr:
probe_polygon_arr = probe_space_row.get(column_added_polygon)
probe_id = probe_space_row.get(column_id)
if probe_id == id or check_is_in_rel_bidirection(space_adjacent, probe_id, id):
continue
for arr_index in range(0, len(x_arr)):
prob_x = x_arr[arr_index]
prob_y = y_arr[arr_index]
is_hit = is_point_in_polygons((prob_x, prob_y), probe_polygon_arr)
if is_hit:
insert_into_rel(space_adjacent, probe_id, id)
break
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)
row[column_added_polygon] = get_outer_polygon_arr(row.get(column_outline))
for sub_arr in space_arr:
if len(sub_arr) == 1:
space_arr.remove(sub_arr)
return space_arr
def calc_space_adjacent(space_list):
space_arr = classify(space_list)
space_adjacent = dict()
space_info = dict()
for space_sub_arr in space_arr:
calc_adjacent_sub_arr(space_sub_arr, space_adjacent, space_info)
return space_adjacent, space_info
with plpy.subtransaction():
sql_str = "SELECT sp.id, sp.project_id, rel.floor_id, sp.object_type, sp.bim_location, sp.outline FROM zone_space_base sp inner join r_sp_in_fl rel on rel.space_id = sp.id where sp.project_id = $1 and rel.project_id = $1 and outline is not null order by floor_id, object_type"
space_data_plan = plpy.prepare(sql_str, ["text"])
space_data = space_data_plan.execute([project_id])
plpy.info("space data : {0}".format(len(space_data)))
rel_data, space_info = calc_space_adjacent(space_data)
delete_plan = plpy.prepare("delete from r_spatial_connection where project_id = $1 and sign = 2 and graph_type = 'SpaceNeighborhood'", ["text"])
delete_plan.execute([project_id])
plpy.info("rel_data : {0}".format(len(rel_data)))
for space_id1, to_space_set in rel_data.items():
space1 = space_info[space_id1]
for space_id2 in to_space_set:
space2 = space_info[space_id2]
delete_duplicate_plan = plpy.prepare("delete from r_spatial_connection where space_id_one = $1 and space_id_two = $2 and type = 'SpaceNeighborhood'", ["text", "text"])
delete_duplicate_plan.execute([space_id1, space_id2])
insert_plan = plpy.prepare("insert into r_spatial_connection(project_id, location_one, location_two, space_id_one, space_id_two, sign, graph_type, floor_id, zone_type) values($1, $2, $3, $4, $5, 2, 'SpaceNeighborhood', $6, $7)", ["text", "text", "text", "text", "text", "text", "text"])
insert_plan.execute([project_id, space1.get(column_bim_location), space2.get(column_bim_location), space_id1, space2.get(column_id), space1.get(column_floor_id), space1.get(column_object_type)])
return True
$BODY$
LANGUAGE plpython3u VOLATILE
COST 100
select public.rel_sp2sp_v2('Pj1101050001')
1. 项目id
select public.rel_sp2sp_v2('Pj1102290002');