jxing %!s(int64=5) %!d(string=hai) anos
pai
achega
ba2f5e5222

+ 72 - 0
datacenter/docs/other/calc_area_v3.md

@@ -0,0 +1,72 @@
+计算业务空间的面积
+## 前置条件
+
+无
+
+## 处理方式
+
+根据业务空间轮廓的格式, 以此计算内部每个小轮廓的面积, 加和在一起为最终结果
+小轮廓面积计算: 计算首个子轮廓面积, 并减去随后所有子轮廓的面积. 结果为小轮廓面积
+
+## 实现方式
+
+
+# 函数
+```
+CREATE OR REPLACE FUNCTION "public"."calc_area_v3"("json_poly" jsonb)
+  RETURNS "pg_catalog"."float8" AS $BODY$
+from shapely.geometry import Polygon
+import json
+
+def meta_polygon_area(single_poly):
+    try:
+        poly_len = len(single_poly)
+        poly = []
+        for i in range(poly_len):
+            pair = single_poly[i]
+            poly.append((pair["X"], pair["Y"]))
+        p = Polygon(poly)
+        return p.area
+    except Exception as e:
+        plpy.info(e)
+        return 0.0
+
+
+def get_area(polygons):
+    length = len(polygons)
+    if length == 0:
+        return 0.0
+    total_area = 0.0
+    for j in range(length):
+        polygon = polygons[j]
+        single_area = meta_polygon_area(polygon)
+        if single_area == 0.0:
+            return 0.0
+        if j == 0:
+            total_area = single_area
+        else:
+            total_area -= single_area
+    return total_area
+
+try:
+    polygon_list = json.loads(json_poly)
+    total_len = len(polygon_list)
+    area = 0.0
+    for index in range(total_len):
+        single_area = get_area(polygon_list[index])
+        if single_area == 0:
+            return None
+        area += single_area
+    return area
+except Exception as e:
+    plpy.info(e)
+    return None
+$BODY$
+  LANGUAGE plpython3u VOLATILE
+  COST 100
+```
+
+## 输入
+    1. 业务空间的轮廓
+## 返回结果
+    面积 float8

+ 157 - 0
datacenter/docs/other/calc_vertically_overlap.md

@@ -0,0 +1,157 @@
+计算所有业务空间, 在竖直方向上的面积重叠关系
+## 前置条件
+只有有轮廓的业务空间才能参与计算
+```
+
+```
+## 处理方式
+
+获取所有建筑, for循环获取每个建筑下所有的业务空间, 按楼层分类
+每个楼层的每个业务空间分别和别的楼层的每个业务空间判断is_vertically_overlap
+将结果是true的两个业务空间保存起来
+删除旧业务空间的垂直交通关系(自动计算的), 添加新关系
+
+## 实现方式
+
+
+# 函数
+```
+create or replace function public.is_vertically_overlap(project_id character varying) returns boolean
+as
+$$
+from shapely.geometry import Polygon
+import json
+
+# 获取Polygon对象
+def get_polygon(single_poly):
+    poly_len = len(single_poly)
+    poly = []
+    for i in range(poly_len):
+        pair = single_poly[i]
+        poly.append((pair["X"], pair["Y"]))
+    return Polygon(poly)
+
+# 在polygon1包含polygon2的时候, 检测是否polygon1内的空洞也包含polygon2
+def is_include(polygon1, poly2):
+    length1 = len(polygon1)
+    for i in range(1, length1):
+        poly1 = get_polygon(polygon1[i])
+        if poly1.overlaps(poly2):
+            return True
+        if poly1.equals(poly2) or poly1.contains(poly2):
+            return False
+    return True
+
+def is_sub_outline_overlap(polygon1, polygon2):
+    poly1 = get_polygon(polygon1[0])
+    poly2 = get_polygon(polygon2[0])
+    if poly1.overlaps(poly2) or poly1.equals(poly2):
+        return True
+    if poly1.contains(poly2):
+        return is_include(polygon1, poly2)
+    if poly2.contains(poly1):
+        return is_include(polygon2, poly1)
+    return False
+
+# 是否垂直方向上面积有重叠
+def is_vertically_overlap(polygon1, polygon2):
+    length1 = len(polygon1)
+    length2 = len(polygon2)
+    if length1 == 0 or length2 == 0:
+        return False
+
+    for i in range(length1):
+        for j in range(length2):
+            if is_sub_outline_overlap(polygon1[i], polygon2[j]):
+                return True
+    return False
+
+
+# building -> floor -> object_type -> [space_id]
+def compose_dict(zone_data):
+    building_map = dict()
+    for row in zone_data:
+        building_id = row['building_id']
+        floor_id = row['floor_id']
+        object_type = row['object_type']
+        if building_id not in building_map:
+            building_map[building_id] = dict()
+        floor_map = building_map[building_id]
+        if floor_id not in floor_map:
+            floor_map[floor_id] = dict()
+        type_map = floor_map[floor_id]
+        if object_type not in type_map:
+            type_map[object_type] = []
+        arr = type_map[object_type]
+        arr.append(row)
+    return building_map
+
+
+try:
+    # 获取所有建筑, for循环获取每个建筑下所有的业务空间, 按楼层分类
+    zone_plan = plpy.prepare("SELECT rel.space_id, fl.building_id, rel.floor_id, rel.object_type, sp.outline FROM r_sp_in_fl rel LEFT JOIN public.floor fl on fl.id = rel.floor_id left join zone_space_base sp on rel.space_id = sp.id where rel.project_id = $1 and sp.outline is not null", ["text"])
+    zone_data = zone_plan.execute([project_id])
+    if len(zone_data) <2:
+        return True
+    row_map = compose_dict(zone_data)
+
+    space_outline_json_map = dict()
+    result_arr = []
+    # 每个楼层的每个业务空间分别和别的楼层的每个业务空间判断is_vertically_overlap
+    # 将结果是true的两个业务空间保存起来
+    for building_id, floor_map in row_map.items():
+        for floor_id, type_map in floor_map.items():
+            for object_type, row_arr in type_map.items():
+                # 要被对比的楼层
+                for other_floor_id in floor_map.keys():
+                    if other_floor_id == floor_id:
+                        continue
+                    other_type_map = floor_map.get(other_floor_id)
+                    if object_type not in other_type_map:
+                        continue
+                    other_row_arr = other_type_map.get(object_type)
+                    for row in row_arr:
+                        for other_row in other_row_arr:
+                            space_id = row['space_id']
+                            other_space_id = other_row['space_id']
+                            if space_id == other_space_id:
+                                continue
+                            if space_id not in space_outline_json_map:
+                                outline_json = json.loads(row['outline'])
+                                space_outline_json_map[space_id] = outline_json
+                            if other_space_id not in space_outline_json_map:
+                                other_outline_json = json.loads(other_row['outline'])
+                                space_outline_json_map[other_space_id] = other_outline_json
+                            outline = space_outline_json_map[space_id]
+                            other_outline = space_outline_json_map[other_space_id]
+                            if is_vertically_overlap(outline, other_outline):
+                                single_result = []
+                                single_result.append(space_id)
+                                single_result.append(other_space_id)
+                                single_result.append(object_type)
+                                result_arr.append(single_result)
+    if len(result_arr) == 0:
+        return True
+    # 删除旧业务空间的垂直交通关系(自动计算的), 添加新关系
+    # 将下面对数据库的操作作为一个事务, 出异常则自动rollback
+    with plpy.subtransaction():
+        del_plan = plpy.prepare("delete from r_sp_vertical_sp where project_id = $1 and sign = 2", ["text"])
+        del_plan.execute([project_id])
+        for single_result in result_arr:
+            del_manual_plan = plpy.prepare("delete from r_sp_vertical_sp where (space_id = $1 and space_other_id = $2) or (space_other_id = $1 and space_id = $2)", ["text", "text"])
+            del_manual_plan.execute([single_result[0], single_result[1]])
+            insert_plan = plpy.prepare("insert into r_sp_vertical_sp(space_id, space_other_id, project_id, sign, object_type) values($1, $2, $3, 2, $4)", ["text", "text", "text", "text"])
+            insert_plan.execute([single_result[0], single_result[1], project_id, single_result[2]])
+    return True
+except Exception as e:
+    plpy.info(e)
+    return False
+$$
+LANGUAGE 'plpython3u' VOLATILE;
+```
+
+## 输入
+    1. 项目id
+## 返回结果
+    true    成功
+    false   失败

+ 106 - 0
datacenter/docs/other/rel_affected_space.md

@@ -0,0 +1,106 @@
+计算当新模型上传之后, 因元空间的变化, 受影响的业务空间
+## 前置条件
+    1. 数据中心楼层绑定了模型服务中的楼层id
+    2. 划分了业务空间
+    3. 该模型上传时, 该楼层上必须已有模型
+    4. 原模型有元空间信息
+```
+```
+
+## 依赖函数
+    无
+## 处理方式
+
+    1. 查询出所有绑定该revit模型楼层的数据中心楼层
+        SELECT id FROM public.floor where model_id = 'revit模型楼层'
+    2. 查询出1中楼层下的所有业务空间
+    3. 获取当前模型和前一个版本的模型id
+    4. 获取3中当前模型和上一个版本模型的所有元空间信息
+    5. 执行get_affected_spaced(space_data, new_ispace_data, prev_ispace_data) 函数
+    
+    get_affected_spaced函数逻辑:
+    1. 根据业务空间的外轮廓和prev_ispace_data的外轮廓是否有重叠来获取 业务空间的原元空间的关系
+    2. 根据元空间的sourceId 来判断删除了哪些元空间
+    3. 根据sourceId匹配出当前模型和上一个模型中都存在的元空间, 根据元空间的外轮廓判断是否发生过改变
+    4. 根据2, 3中改变的元空间来判断 1中获取的关系中哪些业务空间因元空间的改变而发生了改变
+    5. 返回4中得到的业务空间
+
+## 实现方式
+
+```
+```
+
+# 函数
+
+```plpython
+CREATE OR REPLACE FUNCTION rel_affected_space(floor_id character varying) RETURNS boolean AS
+$$
+from relations.src.affected_space.affected_space import get_affected_spaced
+from shapely.geometry import Polygon
+import json
+
+try:
+    # 获取当前楼层绑定的数据中心楼层的id
+    floor_plan = plpy.prepare("SELECT id FROM public.floor where model_id = $1", ["text"])
+    floor_data = floor_plan.execute([floor_id])
+    floor_arr = []
+    for fl_row in floor_data:
+        floor_arr.append(fl_row['id'])
+    
+    if len(floor_arr) == 0:
+        plpy.info("no binding floor")
+        return True
+    floor_str = ""
+    for fl in floor_arr:
+        floor_str = (floor_str + "'" + fl + "',")
+    floor_str = floor_str.strip(',')
+    plpy.info(floor_str)
+    # 获取当前模型和前一个版本的模型id
+    model_plan = plpy.prepare("select id from revit.model_file where model_floor_id = $1 and removed = false and status = 4 and version is not null order by version desc", ["text"])
+    model_data = model_plan.execute([floor_id], 2)
+    if len(model_data) != 2:
+        plpy.info("no previous model")
+        return True
+    sql_str = "select rel.floor_id, rel.space_id, sp.outline from r_sp_in_fl rel left join zone_space_base sp on rel.space_id = sp.id where rel.floor_id in (" + floor_str + ")"
+    # 查询出来的跟模型可能相关的所有业务空间
+    space_data = plpy.execute(sql_str)
+    if len(space_data) == 0:
+        plpy.info("no space relation under binding floor")
+        return True
+    plpy.info('space data count : {0}'.format(len(space_data)))
+    # 获取新模型的元空间
+    new_ispace_plan = plpy.prepare("select source_id, outline from revit.space where model_id = $1", ["text"])
+    new_ispace_data = new_ispace_plan.execute([model_data[0]['id']])
+    plpy.info('new_ispace_data count : {0}'.format(len(new_ispace_data)))
+    # 获取旧模型的所有元空间
+    prev_ispace_plan = plpy.prepare("select source_id, outline from revit.space where model_id = $1", ["text"])
+    prev_ispace_data = prev_ispace_plan.execute([model_data[1]['id']])
+    plpy.info('prev_ispace_data count : {0}'.format(len(prev_ispace_data)))
+    if len(prev_ispace_data) == 0:
+        plpy.info("no prev ispace data")
+        return True
+    
+    affected_space_ids = get_affected_spaced(space_data, new_ispace_data, prev_ispace_data)
+    
+    
+    
+    plpy.info('affected_space_ids count : {0}'.format(len(affected_space_ids)))
+    for space_id in affected_space_ids:
+        space_plan = plpy.prepare("update zone_space_base set state = 1 where id = $1", ["text"])
+        space_plan.execute([space_id])
+    
+    return True
+except Exception as e:
+    plpy.info(e)
+    return False
+$$
+LANGUAGE 'plpython3u' VOLATILE;
+```
+
+
+## 入参
+    1. web模型服务中的floor_id, 格式是uuid
+
+## 返回结果
+    true    成功
+    false   失败